diff --git a/.editorconfig b/.editorconfig index 35f64eb9a3..11c88eec20 100644 --- a/.editorconfig +++ b/.editorconfig @@ -29,9 +29,16 @@ ij_java_names_count_to_use_import_on_demand = 999999 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/main/resources/data/**/*.json] +[purpur-server/src/minecraft/resources/data/**/*.json] indent_size = 2 + +[paper-api-generator/generated/**/*.java] +ij_java_imports_layout = $*,|,* diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24c276e115..c073a77286 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,12 +21,12 @@ jobs: - 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 --no-daemon --stacktrace - name: Build run: ./gradlew build --no-daemon --stacktrace - name: Rebuild on Failure if: ${{ failure() }} run: | - ./gradlew clean cleanCache - ./gradlew applyPatches --no-daemon --stacktrace + ./gradlew clean cleanCache --refresh-dependencies + ./gradlew applyAllPatches --no-daemon --stacktrace ./gradlew build --no-daemon --stacktrace diff --git a/.gitignore b/.gitignore index 1fd301e1d9..230f52dce5 100644 --- a/.gitignore +++ b/.gitignore @@ -50,12 +50,13 @@ manifest.mf *~ # other stuff -run/ - -build-data/ -Purpur-API -Purpur-MojangAPI -Purpur-Server -paper-api-generator +/run + +/purpur-server/build.gradle.kts +/purpur-server/src/minecraft +/paper-server +/purpur-api/build.gradle.kts +/paper-api +/paper-api-generator *.jar test-plugin.settings.gradle.kts diff --git a/.idea/runConfigurations/Run_Purpur_Paperclip_Jar.xml b/.idea/runConfigurations/Run_Purpur_Paperclip_Jar.xml index 3f3ea1dc5f..72f5fa6c49 100644 --- a/.idea/runConfigurations/Run_Purpur_Paperclip_Jar.xml +++ b/.idea/runConfigurations/Run_Purpur_Paperclip_Jar.xml @@ -1,6 +1,6 @@ - - + \ 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 e3255f71da..ef348a313e 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 195cdef25a..b3491f8c05 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__rebuildAllServerPatches_.xml b/.idea/runConfigurations/Upstream_Paper__rebuildAllServerPatches_.xml new file mode 100644 index 0000000000..7e0110e693 --- /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__rebuildPaperApiGeneratorPatches_.xml b/.idea/runConfigurations/Upstream_Paper__rebuildPaperApiGeneratorPatches_.xml new file mode 100644 index 0000000000..b1025a73c8 --- /dev/null +++ b/.idea/runConfigurations/Upstream_Paper__rebuildPaperApiGeneratorPatches_.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 76% rename from .idea/runConfigurations/Upstream_Paper__rebuildPatches_.xml rename to .idea/runConfigurations/Upstream_Paper__rebuildPaperApiPatches_.xml index 0188aeb0f9..ed88625b0a 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 6538e3a358..ae66d546fe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,18 +11,12 @@ 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 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: @@ -42,77 +36,101 @@ 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:21.0.3_9-jdk bash +# docker run -it -v "$(pwd)":/data --rm eclipse-temurin:21.0.5_11-jdk bash Pulling image... root@abcdefg1234:/# javac -version -javac 21.0.3 +javac 21.0.5 ``` ## Understanding Patches -Paper is mostly patches and extensions to 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 different +three different sets of two categories, which are formatted like so: + +Under `purpur-api-generator`: + +- `paper-patches` (applies to the `paper-api/` git repo) + - `sources`: Per-file patches to Paper API Generator classes; + - `features`: Larger feature patches that modify multiple Paper API Generator classes. + +Under `purpur-api`: -- `Purpur-API` - Modifications to `Paper-API`; -- `Purpur-Server` - Modifications to `Paper-Server`. +- `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. -Because the entire structure is based on patches and git, a basic understanding +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, replace the `./` with `.\` 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, `paper-api-generator` for Paper API Generator 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/`, `paper-api-generator/`, 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 +138,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; + +## Adding larger feature patches + +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. -### Method 2 - Fixup commits +There is only a very small chance that you will have to use this system, but adding +such patches is very simple: -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. +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. -This method has the benefit of being able to compile to test your change without -messing with your HEADs. +> ❗ 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,14 +187,14 @@ 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 @@ -170,25 +204,24 @@ messing with your HEADs. 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 existing 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,8 +236,9 @@ when making and submitting changes. ## Formatting -All modifications to non-Purpur files should be marked. The one exception to this is -when modifying javadoc comments, which should not have these markers. +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. - You need to add a comment with a short and identifiable description of the patch: `// Purpur start - ` @@ -216,16 +250,19 @@ when modifying javadoc comments, which should not have these markers. 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.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 - Use plugin-set spawn ``` @@ -236,29 +273,49 @@ into most IDEs and formatters by default. There are a few notes, however: 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. -- `var` usage is heavily 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 +- 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 class in a file not created by the current patch, use the fully qualified class name +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 its only used a couple of times, the FQN is preferred to prevent future +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 org.bukkit.event.Event; +import net.minecraft.server.MinecraftServer; // don't add import here, use FQN like below -public class SomeEvent extends Event { +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. + +## 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/paper.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 @@ -380,12 +438,6 @@ object: return this.level.purpurConfig.useInhabitedTime ? this.inhabitedTime : 0; ``` -#### Committing changes -All changes to the `PurpurConfig` and `PurpurWorldConfig` files -should be done in the commit that created them. So do an interactive rebase -or fixup to apply just those changes to that commit, then add a new commit -that includes the logic that uses that option in the server somewhere. - ## Testing API changes ### Using the Purpur Test Plugin @@ -416,36 +468,9 @@ If you use Maven to build your plugin: ## 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 - [MappingViewer] 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. @@ -455,11 +480,11 @@ file (`CONTRIBUTING.md`), the `LICENSE.md` file, and so forth. This only applies if you're running Windows. If you're running a prior Windows 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 - 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). +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: @@ -473,5 +498,3 @@ everything like usual. > ❗ Do not use the `/mnt/` directory in WSL! Instead, mount the WSL directories > in Windows like described here: > - -[MappingViewer]: https://mappings.cephx.dev/ diff --git a/build-data/dev-imports.txt b/build-data/dev-imports.txt index b818b96e27..1d9862a950 100644 --- a/build-data/dev-imports.txt +++ b/build-data/dev-imports.txt @@ -8,3 +8,7 @@ # 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 +# To import minecraft data files, like the default chat type, use `mc_data` as the prefix: +# mc_data chat_type/chat.json +# mc_data dimension_type/overworld.json +# diff --git a/build-data/purpur.at b/build-data/purpur.at new file mode 100644 index 0000000000..a8a06867e5 --- /dev/null +++ b/build-data/purpur.at @@ -0,0 +1,10 @@ +# 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.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 d472edfdd7..2c5e648da3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,28 +2,54 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { - java - `maven-publish` - id("io.papermc.paperweight.patcher") version "1.7.7" + java // TODO java launcher tasks + id("io.papermc.paperweight.patcher") version "2.0.0-beta.13" } -allprojects { - apply(plugin = "java") +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") + } + patchDir("paperApiGenerator") { + upstreamPath = "paper-api-generator" + patchesDir = file("purpur-api-generator/paper-patches") + outputDir = file("paper-api-generator") + } + } +} + +subprojects { + apply(plugin = "java-library") apply(plugin = "maven-publish") - java { + extensions.configure { toolchain { languageVersion = JavaLanguageVersion.of(21) } } -} - -val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" -subprojects { - tasks.withType().configureEach { + tasks.withType { options.encoding = Charsets.UTF_8.name() options.release = 21 + options.isFork = true } tasks.withType { options.encoding = Charsets.UTF_8.name() @@ -38,64 +64,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.10.3:fat") - decompiler("org.vineflower:vineflower:1.10.1") - paperclip("io.papermc:paperclip:3.0.3") -} - -paperweight { - serverProject = project(":purpur-server") - - remapRepo = paperMavenPublicUrl - decompileRepo = paperMavenPublicUrl - - usePaperUpstream(providers.gradleProperty("paperCommit")) { - withPaperPatcher { - apiPatchDir = layout.projectDirectory.dir("patches/api") - apiOutputDir = layout.projectDirectory.dir("Purpur-API") - - serverPatchDir = layout.projectDirectory.dir("patches/server") - serverOutputDir = layout.projectDirectory.dir("Purpur-Server") - } - - patchTasks.register("generatedApi") { - isBareDirectory = true - upstreamDirPath = "paper-api-generator/generated" - patchDir = layout.projectDirectory.dir("patches/generated-api") - outputDir = layout.projectDirectory.dir("paper-api-generator/generated") - } - } -} - -tasks.generateDevelopmentBundle { - apiCoordinates = "org.purpurmc.purpur:purpur-api" - libraryRepositories = 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" @@ -105,14 +85,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 b179779f7d..a121c72a3b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,9 +2,9 @@ group = org.purpurmc.purpur version = 1.21.4-R0.1-SNAPSHOT mcVersion = 1.21.4 -paperCommit = bb76819589fcd1a7ccac245998ca32f63d99772a +paperCommit = fe75eaf09a22a5cc46877edb714274638d3b111f -org.gradle.caching = true +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.properties b/gradle/wrapper/gradle-wrapper.properties index e2847c8200..cea7a793a8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/patches/api/0003-Build-System-Changes.patch b/patches/api/0003-Build-System-Changes.patch deleted file mode 100644 index ab8b0faa3c..0000000000 --- a/patches/api/0003-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 571534b42cd9c33d6a7bb6fe3bf3a28e33f8e5de..ff4b6b735bf0117845cdb316af115079937792ed 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -162,6 +162,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/0004-Purpur-client-support.patch b/patches/api/0004-Purpur-client-support.patch deleted file mode 100644 index 1a93d29348..0000000000 --- a/patches/api/0004-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 fac4aec289e07231d80a9890653432f688355afa..c8365c38c91b3e6c4f721074f0646fe5adffbdf6 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -3911,4 +3911,13 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM - */ - void sendEntityEffect(org.bukkit.@NotNull EntityEffect effect, @NotNull Entity target); - // Paper end - entity effect API -+ -+ // 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/0005-Default-permissions.patch b/patches/api/0005-Default-permissions.patch deleted file mode 100644 index 66bf743382..0000000000 --- a/patches/api/0005-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/0006-Ridables.patch b/patches/api/0006-Ridables.patch deleted file mode 100644 index 7ecc992d57..0000000000 --- a/patches/api/0006-Ridables.patch +++ /dev/null @@ -1,196 +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/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java -index 19272cff8d6d040e95b2644d70acdac606e06c16..5603ecc5d8f6ccf29333e1c47db3af36379a27d6 100644 ---- a/src/main/java/org/bukkit/entity/Entity.java -+++ b/src/main/java/org/bukkit/entity/Entity.java -@@ -1172,4 +1172,35 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent - */ - void broadcastHurtAnimation(@NotNull java.util.Collection players); - // Paper end - broadcast hurt animation -+ -+ // 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..c31a656daa3df1ab87302d8f14110a828c920102 ---- /dev/null -+++ b/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/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..02de629f066ef7d4898b3053efa957edeea16a3f ---- /dev/null -+++ b/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/patches/api/0007-Allow-inventory-resizing.patch b/patches/api/0007-Allow-inventory-resizing.patch deleted file mode 100644 index da51c8ec8c..0000000000 --- a/patches/api/0007-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 81118a91c2e22e02a1f774d1cc4d3e97064087ce..3ac1e4a821a5b48d3936222cbfddadd3b803deef 100644 ---- a/src/main/java/org/bukkit/event/inventory/InventoryType.java -+++ b/src/main/java/org/bukkit/event/inventory/InventoryType.java -@@ -164,7 +164,7 @@ public enum InventoryType { - SMITHING_NEW(4, "Upgrade Gear", MenuType.SMITHING), - ; - -- 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 MenuType menuType; - private final boolean isCreatable; diff --git a/patches/api/0008-Llama-API.patch b/patches/api/0008-Llama-API.patch deleted file mode 100644 index be6a4ce3a8..0000000000 --- a/patches/api/0008-Llama-API.patch +++ /dev/null @@ -1,137 +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..e34c37579dc8a5a108c03b9eff6bb916a910d867 ---- /dev/null -+++ b/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/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..23ea41ff5dc43a915a263aeb1a246705de8bf9e1 ---- /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.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/patches/api/0009-AFK-API.patch b/patches/api/0009-AFK-API.patch deleted file mode 100644 index 1c3bac8d51..0000000000 --- a/patches/api/0009-AFK-API.patch +++ /dev/null @@ -1,113 +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 c8365c38c91b3e6c4f721074f0646fe5adffbdf6..ed0f892ed987419809fe1a0390b6278c99659919 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -3919,5 +3919,25 @@ 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 -+ * @deprecated Use {@link #resetIdleDuration()} instead -+ */ -+ 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..e9637b82014fe3f4f4671b24d18f77f3d5e4b8ad ---- /dev/null -+++ b/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/patches/api/0010-Bring-back-server-name.patch b/patches/api/0010-Bring-back-server-name.patch deleted file mode 100644 index b88bcca780..0000000000 --- a/patches/api/0010-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 8ab94f8189ebd9d4158231871abdebec399deb2c..c38fa167bbe46dfd34b90b47f6918a399af12c32 100644 ---- a/src/main/java/org/bukkit/Bukkit.java -+++ b/src/main/java/org/bukkit/Bukkit.java -@@ -2968,4 +2968,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 f90da51a8d1003a5cba86decbd42470f7f7e9211..2d17726681b9001179590e9c33ee5b6561fb7789 100644 ---- a/src/main/java/org/bukkit/Server.java -+++ b/src/main/java/org/bukkit/Server.java -@@ -2619,4 +2619,13 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi - */ - void allowPausing(@NotNull org.bukkit.plugin.Plugin plugin, boolean value); - // Paper end - API to check if the server is sleeping -+ -+ // Purpur start -+ /** -+ * Get the name of this server -+ * @return the name of the server -+ */ -+ @NotNull -+ String getServerName(); -+ // Purpur end - } diff --git a/patches/api/0011-ExecuteCommandEvent.patch b/patches/api/0011-ExecuteCommandEvent.patch deleted file mode 100644 index facb1f650e..0000000000 --- a/patches/api/0011-ExecuteCommandEvent.patch +++ /dev/null @@ -1,172 +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 5df19bd701c67506689fc7f49d91f99ebfbc83f0..a09b5458191eb5df4787859b72a37fa1fa2bffba 100644 ---- a/src/main/java/org/bukkit/command/SimpleCommandMap.java -+++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java -@@ -153,6 +153,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); -@@ -162,7 +175,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..55feef2321c7d966c72a33a58cf10136a9cacfa6 ---- /dev/null -+++ b/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/patches/api/0012-Lagging-threshold.patch b/patches/api/0012-Lagging-threshold.patch deleted file mode 100644 index 86737ba982..0000000000 --- a/patches/api/0012-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 c38fa167bbe46dfd34b90b47f6918a399af12c32..cd6c712a2ad92f73c7ce8f4ada8b810fcba00ba3 100644 ---- a/src/main/java/org/bukkit/Bukkit.java -+++ b/src/main/java/org/bukkit/Bukkit.java -@@ -2978,5 +2978,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 2d17726681b9001179590e9c33ee5b6561fb7789..788702d1c5be00a78a0438e267fe5fca9985e4ce 100644 ---- a/src/main/java/org/bukkit/Server.java -+++ b/src/main/java/org/bukkit/Server.java -@@ -2627,5 +2627,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/0013-PlayerSetSpawnerTypeWithEggEvent.patch b/patches/api/0013-PlayerSetSpawnerTypeWithEggEvent.patch deleted file mode 100644 index 64410cdb4c..0000000000 --- a/patches/api/0013-PlayerSetSpawnerTypeWithEggEvent.patch +++ /dev/null @@ -1,184 +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..795c558b481f4e2a550925bd88b8e7d41711456f ---- /dev/null -+++ b/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/src/main/java/org/purpurmc/purpur/event/PlayerSetTrialSpawnerTypeWithEggEvent.java b/src/main/java/org/purpurmc/purpur/event/PlayerSetTrialSpawnerTypeWithEggEvent.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1d4dbf60a182a2a5f93c449e387b82743d20616c ---- /dev/null -+++ b/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/patches/api/0014-Anvil-API.patch b/patches/api/0014-Anvil-API.patch deleted file mode 100644 index 3601d50003..0000000000 --- a/patches/api/0014-Anvil-API.patch +++ /dev/null @@ -1,189 +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 f1f97a85ec713c05c882d7588f4a3e4a017f4795..813f6cd253322538bdf96eb323dd23a7809a1c1e 100644 ---- a/src/main/java/org/bukkit/inventory/AnvilInventory.java -+++ b/src/main/java/org/bukkit/inventory/AnvilInventory.java -@@ -138,4 +138,42 @@ public interface AnvilInventory extends Inventory { - 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/src/main/java/org/bukkit/inventory/view/AnvilView.java b/src/main/java/org/bukkit/inventory/view/AnvilView.java -index 3c1aa1e036bee08304c1cdca59f6a5bc0ba306c0..709fb2d1c7e3253034a651a9f68c003601b598a4 100644 ---- a/src/main/java/org/bukkit/inventory/view/AnvilView.java -+++ b/src/main/java/org/bukkit/inventory/view/AnvilView.java -@@ -89,4 +89,34 @@ public interface AnvilView extends InventoryView { - */ - void bypassEnchantmentLevelRestriction(boolean bypassEnchantmentLevelRestriction); - // Paper end - bypass anvil level restrictions -+ -+ // 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 -+ */ -+ 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 - } -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..b2199854b5c7e74a673cbadbe584e5aaebbe3883 ---- /dev/null -+++ b/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/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..4293c4a57c1c054e8248b7712e8664bd4cb1a972 ---- /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.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/patches/api/0019-Rabid-Wolf-API.patch b/patches/api/0019-Rabid-Wolf-API.patch deleted file mode 100644 index cbf725884a..0000000000 --- a/patches/api/0019-Rabid-Wolf-API.patch +++ /dev/null @@ -1,31 +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/org/bukkit/entity/Wolf.java b/src/main/java/org/bukkit/entity/Wolf.java -index c73489f4b745bc84501ce94f0227b034d9768eae..a97129e71f16ec691759add664bdfd35ab90aaed 100644 ---- a/src/main/java/org/bukkit/entity/Wolf.java -+++ b/src/main/java/org/bukkit/entity/Wolf.java -@@ -108,4 +108,20 @@ public interface Wolf extends Tameable, Sittable, io.papermc.paper.entity.Collar - return Registry.WOLF_VARIANT.getOrThrow(NamespacedKey.minecraft(key)); - } - } -+ -+ // 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/0020-PlayerBookTooLargeEvent.patch b/patches/api/0020-PlayerBookTooLargeEvent.patch deleted file mode 100644 index 248342c518..0000000000 --- a/patches/api/0020-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..31cce9f4e398135016114b96254376325a22ba7c ---- /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.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/patches/api/0021-Full-netherite-armor-grants-fire-resistance.patch b/patches/api/0021-Full-netherite-armor-grants-fire-resistance.patch deleted file mode 100644 index 4f1acab261..0000000000 --- a/patches/api/0021-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 8fdfcbc7d20fe0af6b220ab94516247093637621..f6a8928408e11a5ae723366e4ea1280dfcc6111e 100644 ---- a/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java -+++ b/src/main/java/org/bukkit/event/entity/EntityPotionEffectEvent.java -@@ -216,6 +216,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/0022-Add-EntityTeleportHinderedEvent.patch b/patches/api/0022-Add-EntityTeleportHinderedEvent.patch deleted file mode 100644 index 39b584d99d..0000000000 --- a/patches/api/0022-Add-EntityTeleportHinderedEvent.patch +++ /dev/null @@ -1,138 +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..daf3bbf83ee76322828a38814b483fa2b337bd60 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/event/entity/EntityTeleportHinderedEvent.java -@@ -0,0 +1,114 @@ -+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.ApiStatus; -+import org.jspecify.annotations.NullMarked; -+import org.jspecify.annotations.Nullable; -+ -+/** -+ * Fired when an entity is hindered from teleporting. -+ */ -+@NullMarked -+public class EntityTeleportHinderedEvent extends EntityEvent { -+ private static final HandlerList handlers = new HandlerList(); -+ -+ private final Reason reason; -+ -+ private final @Nullable TeleportCause teleportCause; -+ -+ private boolean retry = false; -+ -+ @ApiStatus.Internal -+ public EntityTeleportHinderedEvent(Entity what, Reason reason, @Nullable TeleportCause teleportCause) { -+ super(what); -+ this.reason = reason; -+ this.teleportCause = teleportCause; -+ } -+ -+ /** -+ * @return why the teleport was hindered. -+ */ -+ 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 -+ public HandlerList getHandlers() { -+ return handlers; -+ } -+ -+ 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/0024-API-for-any-mob-to-burn-daylight.patch b/patches/api/0024-API-for-any-mob-to-burn-daylight.patch deleted file mode 100644 index 2a27a45f48..0000000000 --- a/patches/api/0024-API-for-any-mob-to-burn-daylight.patch +++ /dev/null @@ -1,49 +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 5603ecc5d8f6ccf29333e1c47db3af36379a27d6..a4136da96f0759ca137a86f2518af58bccb6b632 100644 ---- a/src/main/java/org/bukkit/entity/Entity.java -+++ b/src/main/java/org/bukkit/entity/Entity.java -@@ -1202,5 +1202,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 d21a228bbec0302e75c4db5aa1db54f321143587..a4acc3578e935cd1174474bd1f6ff14db4294fe7 100644 ---- a/src/main/java/org/bukkit/entity/LivingEntity.java -+++ b/src/main/java/org/bukkit/entity/LivingEntity.java -@@ -1468,4 +1468,20 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource - */ - boolean canUseEquipmentSlot(org.bukkit.inventory.@NotNull EquipmentSlot slot); - // Paper end - Expose canUseSlot -+ -+ // 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/patches/api/0026-Fix-default-permission-system.patch b/patches/api/0026-Fix-default-permission-system.patch deleted file mode 100644 index 83d8a54ee1..0000000000 --- a/patches/api/0026-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 75b77cc4fe189b4b6baa1af3663dc492e992a266..30b98d1645c571ba5c18e5cc93b0bec3f74b1d3b 100644 ---- a/src/main/java/org/bukkit/permissions/PermissibleBase.java -+++ b/src/main/java/org/bukkit/permissions/PermissibleBase.java -@@ -169,7 +169,7 @@ public class PermissibleBase implements Permissible { - - 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 - Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent); - calculateChildPermissions(perm.getChildren(), false, null); - } -@@ -197,7 +197,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(Locale.ROOT); - - 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/0027-Summoner-API.patch b/patches/api/0027-Summoner-API.patch deleted file mode 100644 index bc94bd07eb..0000000000 --- a/patches/api/0027-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/0030-Added-the-ability-to-add-combustible-items.patch b/patches/api/0030-Added-the-ability-to-add-combustible-items.patch deleted file mode 100644 index 10b834d4c2..0000000000 --- a/patches/api/0030-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 cd6c712a2ad92f73c7ce8f4ada8b810fcba00ba3..79528ad4408eac53754ccc810445970e263a1a32 100644 ---- a/src/main/java/org/bukkit/Bukkit.java -+++ b/src/main/java/org/bukkit/Bukkit.java -@@ -2987,5 +2987,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 788702d1c5be00a78a0438e267fe5fca9985e4ce..c1523229138d5d07569c8e564cf27b8276cca153 100644 ---- a/src/main/java/org/bukkit/Server.java -+++ b/src/main/java/org/bukkit/Server.java -@@ -2634,5 +2634,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/0031-Grindstone-API.patch b/patches/api/0031-Grindstone-API.patch deleted file mode 100644 index b872c1cc49..0000000000 --- a/patches/api/0031-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..d6db2d355553c9c54b83328d237b9c75e7a8e375 ---- /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.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/patches/api/0032-Shears-can-have-looting-enchantment.patch b/patches/api/0032-Shears-can-have-looting-enchantment.patch deleted file mode 100644 index d9c0fb00fd..0000000000 --- a/patches/api/0032-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 bd653ad99e8e75af1494b595640c3910f4d37e6e..13b903e785a9ef5e513cb9d6483482133cc5f25b 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/0033-Lobotomize-stuck-villagers.patch b/patches/api/0033-Lobotomize-stuck-villagers.patch deleted file mode 100644 index 4370acca80..0000000000 --- a/patches/api/0033-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 1db3742024e9cd1b70af2d52b4b756a544c019df..9c722a762c88a88bb5ef18c3b9eab8b371360dac 100644 ---- a/src/main/java/org/bukkit/entity/Villager.java -+++ b/src/main/java/org/bukkit/entity/Villager.java -@@ -367,4 +367,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/0034-Add-local-difficulty-api.patch b/patches/api/0034-Add-local-difficulty-api.patch deleted file mode 100644 index bb9a78b780..0000000000 --- a/patches/api/0034-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 bef54a6c8290e09cbaac20b03dde8dfb902c96b0..3d1e2a6804980c091fe2a66a6810564d317d339f 100644 ---- a/src/main/java/org/bukkit/World.java -+++ b/src/main/java/org/bukkit/World.java -@@ -4246,6 +4246,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/0035-Remove-Timings.patch b/patches/api/0035-Remove-Timings.patch deleted file mode 100644 index 5087679a82..0000000000 --- a/patches/api/0035-Remove-Timings.patch +++ /dev/null @@ -1,159 +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 157617933a772451f6c073d97afaf305769b4d40..438a9c76381ea3f5b774e2232ff56c5dc6f82586 100644 ---- a/src/main/java/co/aikar/timings/TimedEventExecutor.java -+++ b/src/main/java/co/aikar/timings/TimedEventExecutor.java -@@ -80,9 +80,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 4195efcfe044618052bb03dea34a4fb2ca7c44f0..8709c955bac34bc546a8e022cfac808bc61ee793 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 95b7cdf0677ef71e6885fa78aa5c75bb500f5f53..27a02f0c3261067d8e4ee6169c62cecbbfe50d42 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; - } - -@@ -146,7 +146,7 @@ public final class Timings { - */ - public static void setTimingsEnabled(boolean enabled) { - if (enabled && !warnedAboutDeprecationOnEnable) { -- Bukkit.getLogger().severe(PlainTextComponentSerializer.plainText().serialize(deprecationMessage())); -+ //Bukkit.getLogger().severe(PlainTextComponentSerializer.plainText().serialize(deprecationMessage())); - warnedAboutDeprecationOnEnable = true; - } - } -diff --git a/src/main/java/co/aikar/timings/TimingsCommand.java b/src/main/java/co/aikar/timings/TimingsCommand.java -index b83e5ff7ada8771fdf27ba9807c77ba6a4ce12da..f28eec202237461cb489a2b13289d813381a25bc 100644 ---- a/src/main/java/co/aikar/timings/TimingsCommand.java -+++ b/src/main/java/co/aikar/timings/TimingsCommand.java -@@ -47,7 +47,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"); - } - -@@ -57,7 +57,10 @@ public class TimingsCommand extends BukkitCommand { - return true; - } - if (true) { -- sender.sendMessage(Timings.deprecationMessage()); -+ 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 - return true; - } - if (args.length < 1) { -@@ -118,7 +121,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 a09b5458191eb5df4787859b72a37fa1fa2bffba..7740ad53796d08584bb0110f99af5639993e4d71 100644 ---- a/src/main/java/org/bukkit/command/SimpleCommandMap.java -+++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java -@@ -173,10 +173,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/0036-Add-Bee-API.patch b/patches/api/0036-Add-Bee-API.patch deleted file mode 100644 index 940b0126c9..0000000000 --- a/patches/api/0036-Add-Bee-API.patch +++ /dev/null @@ -1,178 +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..7f631a41abee4640a37339a7896ce96e61747735 ---- /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.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/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..e260145d6dc556bbe9e3654296b965c4e393084d ---- /dev/null -+++ b/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/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..8b2b351d620c749cdf58d7e824b55cf55578fde6 ---- /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.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/patches/api/0037-Debug-Marker-API.patch b/patches/api/0037-Debug-Marker-API.patch deleted file mode 100644 index 470a7bff7c..0000000000 --- a/patches/api/0037-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 79528ad4408eac53754ccc810445970e263a1a32..db453d04efb00baaeabb904a7bd1b99dd0a50735 100644 ---- a/src/main/java/org/bukkit/Bukkit.java -+++ b/src/main/java/org/bukkit/Bukkit.java -@@ -3006,5 +3006,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 c1523229138d5d07569c8e564cf27b8276cca153..fcfad39173ecf2573a1ba77236bce8d9f73e02bb 100644 ---- a/src/main/java/org/bukkit/Server.java -+++ b/src/main/java/org/bukkit/Server.java -@@ -2649,5 +2649,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 3d1e2a6804980c091fe2a66a6810564d317d339f..5f7de23e419175e55459df760c7190639ea39f18 100644 ---- a/src/main/java/org/bukkit/World.java -+++ b/src/main/java/org/bukkit/World.java -@@ -4254,6 +4254,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 ed0f892ed987419809fe1a0390b6278c99659919..8cedbba4e65b0a8d3a17c6960033e2a324c64bc9 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -3939,5 +3939,75 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM - * @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(@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/0038-Add-death-screen-API.patch b/patches/api/0038-Add-death-screen-API.patch deleted file mode 100644 index 11afb29cd1..0000000000 --- a/patches/api/0038-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 8cedbba4e65b0a8d3a17c6960033e2a324c64bc9..6f7f1fc3db0237021fa1bd0f11fe56b2d6d4f84a 100644 ---- a/src/main/java/org/bukkit/entity/Player.java -+++ b/src/main/java/org/bukkit/entity/Player.java -@@ -4009,5 +4009,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/0039-Language-API.patch b/patches/api/0039-Language-API.patch deleted file mode 100644 index 5dfa484c1e..0000000000 --- a/patches/api/0039-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..cbdad4cf09c170064a45644efdf7aa0b28608301 ---- /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.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/patches/api/0040-Add-log-suppression-for-LibraryLoader.patch b/patches/api/0040-Add-log-suppression-for-LibraryLoader.patch deleted file mode 100644 index f2b073e13f..0000000000 --- a/patches/api/0040-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 b412aaf08901d169ac9fc89b36f9d6ccb95c53d3..43a0de7394eb786e9c87611003cc820424979205 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 c66252802c51174bc26f266cb5cdecdd856ff220..97f580fccd06a8db5f592a53c8b95a7a6159adac 100644 ---- a/src/main/java/org/bukkit/plugin/java/LibraryLoader.java -+++ b/src/main/java/org/bukkit/plugin/java/LibraryLoader.java -@@ -68,6 +68,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() ); - } - } ); -@@ -94,6 +95,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 -@@ -144,6 +146,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/0041-Fire-Immunity-API.patch b/patches/api/0041-Fire-Immunity-API.patch deleted file mode 100644 index 5de466a439..0000000000 --- a/patches/api/0041-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 a4136da96f0759ca137a86f2518af58bccb6b632..076fe310d500ebb52e705a3a69e895061702f470 100644 ---- a/src/main/java/org/bukkit/entity/Entity.java -+++ b/src/main/java/org/bukkit/entity/Entity.java -@@ -1209,5 +1209,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/0042-Added-goat-ram-event.patch b/patches/api/0042-Added-goat-ram-event.patch deleted file mode 100644 index d3c3f06d3a..0000000000 --- a/patches/api/0042-Added-goat-ram-event.patch +++ /dev/null @@ -1,70 +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..f0a7fe694db145294ff93d320382d1baecc68702 ---- /dev/null -+++ b/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/patches/api/0043-Add-PreExplodeEvents.patch b/patches/api/0043-Add-PreExplodeEvents.patch deleted file mode 100644 index 2699b3c6da..0000000000 --- a/patches/api/0043-Add-PreExplodeEvents.patch +++ /dev/null @@ -1,140 +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..4b4d32c58224e1208f14024ca214078a37550bb5 ---- /dev/null -+++ b/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/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..d56fb066455007cc710f7ba34ba722af6e89bc1d ---- /dev/null -+++ b/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/patches/api/0044-Stored-Bee-API.patch b/patches/api/0044-Stored-Bee-API.patch deleted file mode 100644 index e155a6a0fb..0000000000 --- a/patches/api/0044-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/0045-Explorer-Map-API.patch b/patches/api/0045-Explorer-Map-API.patch deleted file mode 100644 index 91241d8f14..0000000000 --- a/patches/api/0045-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/api/0046-Stonecutter-damage.patch b/patches/api/0046-Stonecutter-damage.patch deleted file mode 100644 index 1ae8744ad5..0000000000 --- a/patches/api/0046-Stonecutter-damage.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Sun, 11 Feb 2024 23:07:47 -0800 -Subject: [PATCH] Stonecutter damage - - -diff --git a/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java b/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java -index d1a5424ff3b289f1c82449ef6d88eb52665df41b..f23b0c250f88926c147af0314b5c4d23c5f8dbae 100644 ---- a/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java -+++ b/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java -@@ -308,7 +308,8 @@ public class EntityDamageEvent extends EntityEvent implements Cancellable { - 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/patches/generated-api/0001-Ridables.patch b/patches/generated-api/0001-Ridables.patch deleted file mode 100644 index 4552c2066c..0000000000 --- a/patches/generated-api/0001-Ridables.patch +++ /dev/null @@ -1,23 +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/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/com/destroystokyo/paper/entity/ai/VanillaGoal.java -index f15a7b4471cd31a487467ec7ecf7a186fa887a51..9b9eb256c4f17693e717e87e62a2e61b928f073a 100644 ---- a/com/destroystokyo/paper/entity/ai/VanillaGoal.java -+++ b/com/destroystokyo/paper/entity/ai/VanillaGoal.java -@@ -441,6 +441,12 @@ public interface VanillaGoal extends Goal { - - GoalKey ZOMBIE_ATTACK_TURTLE_EGG = create("zombie_attack_turtle_egg", Zombie.class); - -+ // 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 -+ - private static GoalKey create(final String key, final Class type) { - return GoalKey.of(type, NamespacedKey.minecraft(key)); - } diff --git a/patches/generated-api/0002-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/patches/generated-api/0002-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch deleted file mode 100644 index 61152fef7d..0000000000 --- a/patches/generated-api/0002-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/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/com/destroystokyo/paper/entity/ai/VanillaGoal.java -index 9b9eb256c4f17693e717e87e62a2e61b928f073a..33eed1c791eeaeb4eba538d32ce8516c5d5fc6c0 100644 ---- a/com/destroystokyo/paper/entity/ai/VanillaGoal.java -+++ b/com/destroystokyo/paper/entity/ai/VanillaGoal.java -@@ -445,6 +445,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 - - private static GoalKey create(final String key, final Class type) { diff --git a/patches/generated-api/0003-Add-option-to-disable-zombie-aggressiveness-towards-.patch b/patches/generated-api/0003-Add-option-to-disable-zombie-aggressiveness-towards-.patch deleted file mode 100644 index b77789f975..0000000000 --- a/patches/generated-api/0003-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/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/com/destroystokyo/paper/entity/ai/VanillaGoal.java -index 33eed1c791eeaeb4eba538d32ce8516c5d5fc6c0..9ef2111b60ace1a088c8c3d4707b26c06f14037c 100644 ---- a/com/destroystokyo/paper/entity/ai/VanillaGoal.java -+++ b/com/destroystokyo/paper/entity/ai/VanillaGoal.java -@@ -447,6 +447,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 - - private static GoalKey create(final String key, final Class type) { diff --git a/patches/generated-api/0004-Rabid-Wolf-API.patch b/patches/generated-api/0004-Rabid-Wolf-API.patch deleted file mode 100644 index 5096e181af..0000000000 --- a/patches/generated-api/0004-Rabid-Wolf-API.patch +++ /dev/null @@ -1,18 +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/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/com/destroystokyo/paper/entity/ai/VanillaGoal.java -index 9ef2111b60ace1a088c8c3d4707b26c06f14037c..6eea8fbe575c4570c2b760c9828f8ec5b81c7202 100644 ---- a/com/destroystokyo/paper/entity/ai/VanillaGoal.java -+++ b/com/destroystokyo/paper/entity/ai/VanillaGoal.java -@@ -449,6 +449,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 - - private static GoalKey create(final String key, final Class type) { diff --git a/patches/generated-api/0005-Iron-golem-poppy-calms-anger.patch b/patches/generated-api/0005-Iron-golem-poppy-calms-anger.patch deleted file mode 100644 index bae54e389e..0000000000 --- a/patches/generated-api/0005-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/com/destroystokyo/paper/entity/ai/VanillaGoal.java b/com/destroystokyo/paper/entity/ai/VanillaGoal.java -index 6eea8fbe575c4570c2b760c9828f8ec5b81c7202..e7795889133063ac165d5b4783ddcbcbb442f189 100644 ---- a/com/destroystokyo/paper/entity/ai/VanillaGoal.java -+++ b/com/destroystokyo/paper/entity/ai/VanillaGoal.java -@@ -450,6 +450,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 - - private static GoalKey create(final String key, final Class type) { diff --git a/patches/server/0002-Purpur-config-files.patch b/patches/server/0002-Purpur-config-files.patch deleted file mode 100644 index 1a0d459acd..0000000000 --- a/patches/server/0002-Purpur-config-files.patch +++ /dev/null @@ -1,532 +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 8f62879582195d8ae4f64bd23f752fa133b1c973..be1bb14dca9367b9685841985b6198376986c496 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("Purpur", serverUUID, logFailedRequests, Bukkit.getLogger()); // Pufferfish // Purpur - Purpur config files - - metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { - String minecraftVersion = Bukkit.getVersion(); -@@ -601,16 +601,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-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/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java -index 13bd145b1e8006a53c22f5dc0c78f29b540c7663..7b2daf47e411362a462019a1612a99c952170200 100644 ---- a/src/main/java/net/minecraft/commands/CommandSourceStack.java -+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java -@@ -312,6 +312,30 @@ public class CommandSourceStack implements ExecutionCommandSource io.papermc.paper.adventure.PaperAdventure.asVanilla(message), broadcastToOps); -+ } -+ // Purpur end - Purpur config files -+ - 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 17a158ff6ce6520b69a5a0032ba4c05449dd0cf8..cf63c64b8c2ac148b83325209940713a91b91bad 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -235,6 +235,15 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - 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.setPvpAllowed(dedicatedserverproperties.pvp); -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 27f9d167b5ae9ce5117798ea44324107df59425f..f3c5e076558cd8d7b9d9b3212872f8675670b2dd 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -175,6 +175,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - // Paper end - add paper world config - - public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray -+ public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur - Purpur config files - public static BlockPos lastPhysicsProblem; // Spigot - private org.spigotmc.TickLimiter entityLimiter; - private org.spigotmc.TickLimiter tileLimiter; -@@ -843,6 +844,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - // Paper end - getblock optimisations - cache world height/sections - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).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) worlddatamutable).getLevelName(), env); // Purpur - Purpur config files - 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 97b5d6ba2b19a7c730730c74175a29157aed1840..cc2f23613644126c3f7506b26db8e6a865f78dde 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1086,6 +1086,7 @@ public final class CraftServer implements Server { - - 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 && config.spawnMonsters); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) -@@ -1101,6 +1102,7 @@ public final class CraftServer implements Server { - } - } - world.spigotConfig.init(); // Spigot -+ world.purpurConfig.init(); // Purpur - Purpur config files - } - - Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper -@@ -1118,6 +1120,7 @@ public final class CraftServer implements Server { - 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"); - -@@ -3031,6 +3034,18 @@ public final class CraftServer implements Server { - 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() { - 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 1c2439ffc1e407ff69286817d22f127470ce07ba..d313f3a9b31d4ecc3b48f8fc2e44d3b445e8e2c5 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 - Purpur config files -+ 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 - // 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..c2991c34fd4306fae79fca2c1349c826b3247c49 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -0,0 +1,178 @@ -+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.Registry; -+import net.minecraft.core.registries.BuiltInRegistries; -+import net.minecraft.core.registries.Registries; -+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 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 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", 38); -+ set("config-version", 38); -+ -+ 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(); -+ } -+} -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..42e502cfcb8d2e775cbf738773caf1a087d2f3f4 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -0,0 +1,93 @@ -+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.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.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/0003-Purpur-client-support.patch b/patches/server/0003-Purpur-client-support.patch deleted file mode 100644 index 3f727f26ea..0000000000 --- a/patches/server/0003-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 fc7f7a34babd095a51b5321f600aef65a2a9d123..be20eed6e3bfab7a78228dfb42b50f80ad3d817c 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -327,6 +327,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - public com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent - public @Nullable String clientBrandName = null; // Paper - Brand support - public org.bukkit.event.player.PlayerQuitEvent.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 -+ public boolean purpurClient = false; // Purpur - Purpur client support - - // Paper start - rewrite chunk system - private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader; -diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -index b0bc66dc7248aae691dcab68b925b52a1695e63f..92749b57d3a2b2ffee79436319513248846296b6 100644 ---- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -@@ -85,6 +85,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - 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 ResourceLocation MINECRAFT_BRAND = ResourceLocation.withDefaultNamespace("brand"); // Paper - Brand support -+ protected static final ResourceLocation PURPUR_CLIENT = ResourceLocation.fromNamespaceAndPath("purpur", "client"); // Purpur - Purpur client support - - public ServerCommonPacketListenerImpl(MinecraftServer minecraftserver, Connection networkmanager, CommonListenerCookie commonlistenercookie, ServerPlayer player) { // CraftBukkit - this.server = minecraftserver; -@@ -179,6 +180,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t register custom payload", ex); - this.disconnect(Component.literal("Invalid payload REGISTER!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause - } -+ // Purpur start - Purpur client support -+ } else if (identifier.equals(PURPUR_CLIENT)) { -+ try { -+ player.purpurClient = true; -+ } catch (Exception ignore) { -+ } -+ // Purpur end - Purpur client support - } else if (identifier.equals(ServerCommonPacketListenerImpl.CUSTOM_UNREGISTER)) { - try { - String channels = payload.toString(com.google.common.base.Charsets.UTF_8); -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 6a647cab8b2e476987931486e290703b8726f2c7..c5bd2a45b32e8dff83c148379544db125684622a 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -3565,4 +3565,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundEntityEventPacket(((CraftEntity) target).getHandle(), effect.getData())); - } - // Paper end - entity effect API -+ -+ // Purpur start - Purpur client support -+ @Override -+ public boolean usesPurpurClient() { -+ return getHandle().purpurClient; -+ } -+ // Purpur end - Purpur client support - } diff --git a/patches/server/0004-MC-Utils.patch b/patches/server/0004-MC-Utils.patch deleted file mode 100644 index 022fd8ea51..0000000000 --- a/patches/server/0004-MC-Utils.patch +++ /dev/null @@ -1,164 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Tue, 9 Jun 2020 03:17:25 -0400 -Subject: [PATCH] MC Utils - - -diff --git a/src/main/java/org/purpurmc/purpur/util/MinecraftInternalPlugin.java b/src/main/java/org/purpurmc/purpur/util/MinecraftInternalPlugin.java -new file mode 100644 -index 0000000000000000000000000000000000000000..129acb8ad139decc6b1c023cb10bc32dc91d64d1 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/util/MinecraftInternalPlugin.java -@@ -0,0 +1,152 @@ -+package org.purpurmc.purpur.util; -+ -+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.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; -+ } -+ // Paper start -+ @Override -+ public io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() { -+ return pdf; -+ } -+ // Paper end -+ -+ @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."); -+ } -+ -+ // Paper start - lifecycle events -+ @Override -+ public @NotNull io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager getLifecycleManager() { -+ throw new UnsupportedOperationException("Not supported."); -+ } -+ // Paper end - lifecycle events -+} diff --git a/patches/server/0005-Fix-decompile-errors.patch b/patches/server/0005-Fix-decompile-errors.patch deleted file mode 100644 index f16d294f48..0000000000 --- a/patches/server/0005-Fix-decompile-errors.patch +++ /dev/null @@ -1,52 +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/commands/execution/tasks/BuildContexts.java b/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java -index b0d26b0eadb2a43924629424a6c13198aace8f69..9f5c3ec2eae9b30bdb8dbcb328d7f701cb7aeb9d 100644 ---- a/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java -+++ b/src/main/java/net/minecraft/commands/execution/tasks/BuildContexts.java -@@ -52,7 +52,7 @@ public class BuildContexts> { - } - - RedirectModifier redirectModifier = commandContext.getRedirectModifier(); -- if (redirectModifier instanceof CustomModifierExecutor customModifierExecutor) { -+ if (redirectModifier instanceof CustomModifierExecutor customModifierExecutor) { // Purpur - decompile error - customModifierExecutor.apply(baseSource, list, contextChain, chainModifiers, ExecutionControl.create(context, frame)); - return; - } -@@ -92,11 +92,11 @@ public class BuildContexts> { - - if (list.isEmpty()) { - if (chainModifiers.isReturn()) { -- context.queueNext(new CommandQueueEntry<>(frame, FallthroughTask.instance())); -+ context.queueNext(new CommandQueueEntry<>(frame, (EntryAction) FallthroughTask.instance())); // Purpur - decompile error - } - } else { - CommandContext commandContext2 = contextChain.getTopContext(); -- if (commandContext2.getCommand() instanceof CustomCommandExecutor customCommandExecutor) { -+ if (commandContext2.getCommand() instanceof CustomCommandExecutor customCommandExecutor) { // Purpur - decompile error - ExecutionControl executionControl = ExecutionControl.create(context, frame); - - for (T executionCommandSource2 : list) { -diff --git a/src/main/java/net/minecraft/commands/synchronization/ArgumentTypeInfos.java b/src/main/java/net/minecraft/commands/synchronization/ArgumentTypeInfos.java -index 7b118a92a6eb779f800ae8f5d8f6e3c861fc4f6a..057a038e8dcacd7496a0b2373de2c20255a5c297 100644 ---- a/src/main/java/net/minecraft/commands/synchronization/ArgumentTypeInfos.java -+++ b/src/main/java/net/minecraft/commands/synchronization/ArgumentTypeInfos.java -@@ -119,10 +119,10 @@ public class ArgumentTypeInfos { - register(registry, "dimension", DimensionArgument.class, SingletonArgumentInfo.contextFree(DimensionArgument::dimension)); - register(registry, "gamemode", GameModeArgument.class, SingletonArgumentInfo.contextFree(GameModeArgument::gameMode)); - register(registry, "time", TimeArgument.class, new TimeArgument.Info()); -- register(registry, "resource_or_tag", fixClassType(ResourceOrTagArgument.class), new ResourceOrTagArgument.Info()); -- register(registry, "resource_or_tag_key", fixClassType(ResourceOrTagKeyArgument.class), new ResourceOrTagKeyArgument.Info()); -- register(registry, "resource", fixClassType(ResourceArgument.class), new ResourceArgument.Info()); -- register(registry, "resource_key", fixClassType(ResourceKeyArgument.class), new ResourceKeyArgument.Info()); -+ register(registry, "resource_or_tag", fixClassType(ResourceOrTagArgument.class), new ResourceOrTagArgument.Info<>()); // Purpur - decompile error -+ register(registry, "resource_or_tag_key", fixClassType(ResourceOrTagKeyArgument.class), new ResourceOrTagKeyArgument.Info<>()); // Purpur - decompile error -+ register(registry, "resource", fixClassType(ResourceArgument.class), new ResourceArgument.Info<>()); // Purpur - decompile error -+ register(registry, "resource_key", fixClassType(ResourceKeyArgument.class), new ResourceKeyArgument.Info<>()); // Purpur - decompile error - register(registry, "template_mirror", TemplateMirrorArgument.class, SingletonArgumentInfo.contextFree(TemplateMirrorArgument::templateMirror)); - register(registry, "template_rotation", TemplateRotationArgument.class, SingletonArgumentInfo.contextFree(TemplateRotationArgument::templateRotation)); - register(registry, "heightmap", HeightmapTypeArgument.class, SingletonArgumentInfo.contextFree(HeightmapTypeArgument::heightmap)); diff --git a/patches/server/0006-Component-related-conveniences.patch b/patches/server/0006-Component-related-conveniences.patch deleted file mode 100644 index e463f84ccb..0000000000 --- a/patches/server/0006-Component-related-conveniences.patch +++ /dev/null @@ -1,103 +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 be20eed6e3bfab7a78228dfb42b50f80ad3d817c..09a7809b89a4f302a1149850d4daeb471365d189 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -2236,6 +2236,26 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - 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(@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 - Component related conveniences -+ - @Override - public void displayClientMessage(Component message, boolean overlay) { - this.sendSystemMessage(message, overlay); -@@ -2457,6 +2477,20 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - return new CommandSourceStack(this.commandSource(), this.position(), this.getRotationVector(), this.serverLevel(), this.getPermissionLevel(), this.getName().getString(), this.getDisplayName(), this.server, this); - } - -+ // 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(@Nullable net.kyori.adventure.text.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); - } -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 9b71655a425356132afb786eff623f558e1e3498..98e803eaf5ce4c773f35fd752c21c7176707427c 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -991,6 +991,20 @@ public abstract class PlayerList { - } - // CraftBukkit end - -+ // 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(@Nullable net.kyori.adventure.text.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) { - 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 bb1a60180e58c1333e7bb33e8acf1b0225eda8a8..ab0ba4406dcaa915435c3f53ac9ca06fb21c673b 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -@@ -198,6 +198,15 @@ public class DamageSource { - } - } - -+ // 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/patches/server/0007-Ridables.patch b/patches/server/0007-Ridables.patch deleted file mode 100644 index 9ebf529d2a..0000000000 --- a/patches/server/0007-Ridables.patch +++ /dev/null @@ -1,6681 +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 faffd87c357511ef00646971a16acf1009362c59..6714b4a39180affd101f1cab0d587cf2d3e6886a 100644 ---- a/src/main/java/net/minecraft/core/BlockPos.java -+++ b/src/main/java/net/minecraft/core/BlockPos.java -@@ -63,6 +63,12 @@ public class BlockPos extends Vec3i { - 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/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index ae4ebf509837e8d44255781c61d02873f8b74be8..ce4ce361061932162ace58070d44d1aa70189dbd 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1857,6 +1857,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - Add EntityMoveEvent - net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers - worldserver.updateLagCompensationTick(); // Paper - lag compensation -+ worldserver.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur - Ridables - - gameprofilerfiller.push(() -> { - String s = String.valueOf(worldserver); -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 1f898500d0e9b18a880645ceb0a8ff0fe75f4e48..4b9434b2d03cd24f5dac7098d2f1318fd12baddb 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -232,6 +232,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 - - public LevelChunk getChunkIfLoaded(int x, int z) { - return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 09a7809b89a4f302a1149850d4daeb471365d189..8207208d6fb3f982e9909add9e74a0dda69e8120 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1030,6 +1030,15 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - this.trackEnteredOrExitedLavaOnVehicle(); - this.updatePlayerAttributes(); - this.advancements.flushDirty(this); -+ -+ // 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/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 84fa24880d02dc7ba1ec8bda3575be38447fd4b2..fbc59503316d566e88b037851afd74e5469c281b 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2887,6 +2887,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - - ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event); - -+ player.processClick(enumhand); // Purpur - Ridables -+ - // 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.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.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 7ac7d0729705cb02f22277be3c467aed4f69ec0e..5c14180d92e1baebe59b08311746418e7d9f6a24 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -340,7 +340,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - private final Set tags; - private final double[] pistonDeltas; - private long pistonDeltasGameTime; -- private EntityDimensions dimensions; -+ protected EntityDimensions dimensions; // Purpur - private -> protected - Ridables - private float eyeHeight; - public boolean isInPowderSnow; - public boolean wasInPowderSnow; -@@ -3324,6 +3324,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - 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 -+ - this.gameEvent(GameEvent.ENTITY_MOUNT, passenger); - } - } -@@ -3363,6 +3370,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - 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) == entity) { - this.passengers = ImmutableList.of(); - } else { -@@ -5355,4 +5370,44 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - return ((net.minecraft.server.level.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, 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 - Ridables - } -diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java -index 397765b1547ae47b64963b3807b206c50a6650e1..293ffe990de70f4f8872f063388a3a50c60b68e6 100644 ---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java -+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java -@@ -25,6 +25,19 @@ public class GlowSquid extends Squid { - super(type, world); - } - -+ // 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/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 96b4fbe4a4655777ff10b32e3257e2fac2aba12a..715b76bd0ccc0c29583a55f82a8ecd889ab49b56 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -246,9 +246,9 @@ public abstract class LivingEntity extends Entity implements Attackable { - protected float rotOffs; - 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; -@@ -323,7 +323,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.lastClimbablePos = Optional.empty(); - this.activeLocationDependentEnchantments = new EnumMap(EquipmentSlot.class); - this.appliedScale = 1.0F; -- this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type)); -+ this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur - Ridables - 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()); -@@ -373,6 +373,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).add(Attributes.MAX_ABSORPTION).add(Attributes.STEP_HEIGHT).add(Attributes.SCALE).add(Attributes.GRAVITY).add(Attributes.SAFE_FALL_DISTANCE).add(Attributes.FALL_DAMAGE_MULTIPLIER).add(Attributes.JUMP_STRENGTH).add(Attributes.OXYGEN_BONUS).add(Attributes.BURNING_TIME).add(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE).add(Attributes.WATER_MOVEMENT_EFFICIENCY).add(Attributes.MOVEMENT_EFFICIENCY).add(Attributes.ATTACK_KNOCKBACK); - } -+ public boolean shouldSendAttribute(Attribute attribute) { return true; } // Purpur - Ridables - - @Override - protected void checkFallDamage(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) { -@@ -3703,8 +3704,10 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.pushEntities(); - gameprofilerfiller.pop(); - // Paper start - Add EntityMoveEvent -- 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 - 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 net.minecraft.world.entity.player.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()); -@@ -3714,6 +3717,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 - 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(), (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 - Ridables - } - // Paper end - Add EntityMoveEvent - world = this.level(); -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 5a0b51342f4a646101f4588697bcae7d1ca8a010..261288f51ed47b0eac80cc965c76683f3e13687f 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -159,8 +159,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - this.restrictRadius = -1.0F; - 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(world); -@@ -1496,7 +1496,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - protected void onOffspringSpawnedFromEgg(Player player, Mob child) {} - - protected InteractionResult mobInteract(Player player, InteractionHand hand) { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - Ridables - } - - public boolean isWithinRestriction() { -@@ -1816,4 +1816,58 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - public float[] getArmorDropChances() { - return this.armorDropChances; - } -+ -+ // 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/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..d6c98612ca15e506657d85f6872c1278e0b73652 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,14 +23,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 defaultAttributes) { -+ // Purpur start - Ridables -+ this(defaultAttributes, 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); - } - } -@@ -44,7 +51,7 @@ public class AttributeMap { - } - - public Collection getSyncableAttributes() { -- return this.attributes.values().stream().filter(attribute -> attribute.getAttribute().value().isClientSyncable()).collect(Collectors.toList()); -+ return this.attributes.values().stream().filter(attribute -> attribute.getAttribute().value().isClientSyncable() && (entity == null || entity.shouldSendAttribute(attribute.getAttribute().value()))).collect(Collectors.toList()); // Purpur - Ridables - } - - @Nullable -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 c76438d5ce2330eca16dc0b381f97e9506f84aef..8ccbf0386aa453e82fc0f82d2aefd1e08b6c3345 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 -@@ -131,7 +131,7 @@ public class DefaultAttributes { - .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.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/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java b/src/main/java/net/minecraft/world/entity/ai/control/MoveControl.java -index c8fd5696de7c3623cdb4f498190a5c2708cf843e..2a6e5a9b35102ef540b561ec7ef5a5f119c564fe 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 - 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/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java b/src/main/java/net/minecraft/world/entity/ai/control/SmoothSwimmingLookControl.java -index fbfc2f2515ad709b2c1212aef9521e795547d66b..ebe941aeb959fc34372bfc59bc3a13421167b4cf 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 - 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(yaw -> this.mob.yHeadRot = this.rotateTowards(this.mob.yHeadRot, yaw + 20.0F, this.yMaxRotSpeed)); -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..add1c146cd7428547d9ef8810841b4cf39a6a05e 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -47,12 +47,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 - Ridables - if (!world.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(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() && (float) this.tickCount % 10.0F == 0.0F; -@@ -102,7 +149,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 - Ridables - } - - public boolean isResting() { -@@ -135,6 +182,14 @@ public class Bat extends AmbientCreature { - - @Override - protected void customServerAiStep(ServerLevel world) { -+ // 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(world); - 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 9aedc62b1766f6a7db4da7eba55167d21d698791..d1fa6b6a18bd7a44e398eed17f2ff127b09f222a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java -+++ b/src/main/java/net/minecraft/world/entity/animal/AbstractFish.java -@@ -87,6 +87,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::test)); - this.goalSelector.addGoal(4, new AbstractFish.FishSwimGoal(this)); -@@ -100,7 +101,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { - @Override - public void travel(Vec3 movementInput) { - if (this.isControlledByLocalInstance() && this.isInWater()) { -- this.moveRelative(0.01F, movementInput); -+ this.moveRelative(getRider() != null ? getSpeed() : 0.01F, movementInput); // Purpur - Ridables - this.move(MoverType.SELF, this.getDeltaMovement()); - this.setDeltaMovement(this.getDeltaMovement().scale(0.9)); - if (this.getTarget() == null) { -@@ -161,7 +162,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 - Ridables - private final AbstractFish fish; - - FishMoveControl(AbstractFish owner) { -@@ -169,14 +170,22 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { - this.fish = owner; - } - -+ // 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 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 0bafe14342c1acce131ad34717c18aed3718deed..13f6e4c83e1775daadb13e3532d7dfe6eef15aac 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -154,6 +154,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 - Ridables - // Paper start - Fix MC-167279 - class BeeFlyingMoveControl extends FlyingMoveControl { - public BeeFlyingMoveControl(final Mob entity, final int maxPitchChange, final boolean noGravity) { -@@ -162,11 +163,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 -@@ -178,6 +192,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); -@@ -192,6 +240,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.399999976158142D, true)); - this.goalSelector.addGoal(1, new Bee.BeeEnterHiveGoal()); - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); -@@ -211,6 +260,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(new Class[0])); - this.targetSelector.addGoal(2, new Bee.BeeBecomeAngryTargetGoal(this)); - this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true)); -@@ -738,16 +788,16 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - return state.is(BlockTags.BEE_ATTRACTIVE) ? ((Boolean) state.getValueOrElse(BlockStateProperties.WATERLOGGED, false) ? false : (state.is(Blocks.SUNFLOWER) ? state.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.UPPER : true)) : false; - } - -- private class BeeLookControl extends LookControl { -+ private class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables - - BeeLookControl(final Mob entity) { - super(entity); - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - Ridables - if (!Bee.this.isAngry()) { -- super.tick(); -+ super.vanillaTick(); // Purpur - Ridables - } - } - -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 989b7be74eaeba7f40eac87c7ee7f252cb0c05c9..472bbf4c3f932e2b1c7d7fa3c74b41f5be11431f 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -100,12 +100,38 @@ public class Cat extends TamableAnimal implements VariantHolder { - return itemstack.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.5D)); - this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this)); - this.goalSelector.addGoal(3, new Cat.CatRelaxOnOwnerGoal(this)); -@@ -118,6 +144,7 @@ public class Cat extends TamableAnimal implements VariantHolder(this, Rabbit.class, false, (TargetingConditions.Selector) null)); - this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR)); - } -@@ -375,6 +402,7 @@ public class Cat extends TamableAnimal implements VariantHolder { -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..fcf7073dd2d79f1483bdc6e7fdc37c8c260ae418 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 - 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/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index 3e00bbff266fc71b07014e7e047d77b7f809239f..dc7ccfe90a82892d65098a325fd71fbbc734da86 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -44,9 +44,27 @@ public class Cow extends Animal { - super(type, world); - } - -+ // 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 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.0D)); - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); - this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, (itemstack) -> { -@@ -94,6 +112,7 @@ public class Cow extends Animal { - - @Override - public InteractionResult mobInteract(Player player, InteractionHand hand) { -+ if (getRider() != null) return InteractionResult.PASS; // Purpur - Ridables - ItemStack itemstack = player.getItemInHand(hand); - - if (itemstack.is(Items.BUCKET) && !this.isBaby()) { -@@ -102,7 +121,7 @@ public class Cow extends Animal { - - if (event.isCancelled()) { - player.containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - Ridables - } - // 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 5af4d590a9b0f17ba53c6959d9c18bd1269878a4..af677b6581514a07e6455977ffc591538d43bbc6 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -85,14 +85,82 @@ public class Dolphin extends AgeableWaterCreature { - return !entityitem.hasPickUpDelay() && entityitem.isAlive() && entityitem.isInWater(); - }; - public static final float BABY_SCALE = 0.65F; -+ private int spitCooldown; // Purpur - Ridables - - public Dolphin(EntityType type, Level world) { - super(type, world); -- 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.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 - Ridables -+ - @Nullable - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData entityData) { -@@ -177,6 +245,7 @@ public class Dolphin extends AgeableWaterCreature { - 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 - Ridables - 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)); -@@ -187,6 +256,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.0D, 1.0D)); -+ this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Guardian.class})).setAlertOthers()); - } - -@@ -231,7 +301,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 -@@ -264,6 +334,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/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java -index d48c2bdb004c86e9e08680138fe51dc3b2975a64..ce5ac300582f61d0f3eeb1e94340cfefbdff1ba9 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -144,6 +144,44 @@ public class Fox extends Animal implements VariantHolder { - 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); -@@ -163,6 +201,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 - 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.2D)); -@@ -189,6 +228,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 - Ridables - this.targetSelector.addGoal(3, new Fox.DefendTrustedTargetGoal(LivingEntity.class, false, false, (entityliving, worldserver) -> { - return Fox.TRUSTED_TARGET_SELECTOR.test(entityliving) && !this.trusts(entityliving.getUUID()); - })); -@@ -754,16 +794,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 - 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 - } - - } -@@ -774,16 +814,16 @@ public class Fox extends Animal implements VariantHolder { - } - } - -- private class FoxMoveControl extends MoveControl { -+ private 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/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index e07b79ef172095c1800c88342b3ac8dc7703aea2..938a0c6f7cfbb6cd459d5a2ec46f912d45fd2226 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -62,8 +62,27 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - super(type, world); - } - -+ // 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 (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.0D, true)); - this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9D, 32.0F)); - this.goalSelector.addGoal(2, new MoveBackToVillageGoal(this, 0.6D, false)); -@@ -71,6 +90,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, new Class[0])); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt)); -@@ -267,13 +287,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 - Ridables - } else { - float f = this.getHealth(); - - this.heal(25.0F); - if (this.getHealth() == f) { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - Ridables - } 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 b04532aa04aec6ebbff74d64abb73189c2e12016..f37c8efa34efcb289bbeed06ea2d3860ff2662ac 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -64,6 +64,23 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder(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 be753557d7ebd6f1e82b1bdb6d60ecc450f72eec..20d18f7bd8e9c1b3e3a6d06a11c9072456cd742f 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Panda.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Panda.java -@@ -112,6 +112,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(); -@@ -271,6 +297,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.0D)); - this.goalSelector.addGoal(2, new Panda.PandaBreedGoal(this, 1.0D)); - this.goalSelector.addGoal(3, new Panda.PandaAttackGoal(this, 1.2000000476837158D, true)); -@@ -288,6 +315,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 - Ridables - this.targetSelector.addGoal(1, (new Panda.PandaHurtByTargetGoal(this, new Class[0])).setAlertOthers(new Class[0])); - } - -@@ -640,7 +668,7 @@ public class Panda extends Animal { - ItemStack itemstack = 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; -@@ -679,12 +707,12 @@ public class Panda extends Animal { - } - } - -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - Ridables - } - - return InteractionResult.SUCCESS_SERVER; - } else { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - Ridables - } - } - -@@ -729,7 +757,7 @@ public class Panda extends Animal { - return itemEntity.getItem().is(ItemTags.PANDA_EATS_FROM_GROUND) && itemEntity.isAlive() && !itemEntity.hasPickUpDelay(); - } - -- private static class PandaMoveControl extends MoveControl { -+ private static class PandaMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables - - private final Panda panda; - -@@ -739,9 +767,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/src/main/java/net/minecraft/world/entity/animal/Parrot.java b/src/main/java/net/minecraft/world/entity/animal/Parrot.java -index a2f0b79599799ad2aa85aff821d8ac76a8e650bd..872f2406531bf71f378325441b7215c085f1a70d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Parrot.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Parrot.java -@@ -125,12 +125,68 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder type, Level world) { - super(type, world); -- 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 -+ - @Nullable - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData entityData) { -@@ -149,8 +205,10 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder { -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 cd72d8f766069796ce1fe4a83b8646692005ff8c..6f30cdc26054a4ed7c577cd4e9aa29ade1d2ede5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java -+++ b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java -@@ -59,11 +59,40 @@ public class PolarBear extends Animal implements NeutralMob { - private int remainingPersistentAngerTime; - @Nullable - private UUID persistentAngerTarget; -+ private int standTimer = 0; // Purpur - Ridables - - public PolarBear(EntityType type, Level world) { - super(type, world); - } - -+ // 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 -+ - @Nullable - @Override - public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) { -@@ -79,6 +108,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, polarBear -> polarBear.isBaby() ? DamageTypeTags.PANIC_CAUSES : DamageTypeTags.PANIC_ENVIRONMENTAL_CAUSES)); -@@ -86,6 +116,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)); -@@ -204,6 +235,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 -@@ -223,6 +260,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 - Ridables - } - - 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 cdb74f86ee92ee143af29962a85d45ca585cee44..93c1b945672a769ef6ee285efdcebd3717caf9f1 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java -@@ -52,6 +52,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/src/main/java/net/minecraft/world/entity/animal/Rabbit.java b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -index 53d60d62686f9b6bc98b6b25e4315b848600a99d..b5601b99401f2c3cf4ce0fef4497b09667083412 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -@@ -88,6 +88,7 @@ public class Rabbit extends Animal implements VariantHolder { - private boolean wasOnGround; - private int jumpDelayTicks; - public int moreCarrotTicks; -+ private boolean actualJump; // Purpur - Ridables - - public Rabbit(EntityType type, Level world) { - super(type, world); -@@ -95,9 +96,55 @@ public class Rabbit extends Animal implements VariantHolder { - this.moveControl = new Rabbit.RabbitMoveControl(this); - } - -+ // 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.2D)); - this.goalSelector.addGoal(2, new BreedGoal(this, 0.8D)); -@@ -114,6 +161,14 @@ public class Rabbit extends Animal implements VariantHolder { - - @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.6D) { -@@ -188,6 +243,12 @@ public class Rabbit extends Animal implements VariantHolder { - - @Override - public void customServerAiStep(ServerLevel world) { -+ // Purpur start - Ridables -+ if (getRider() != null && this.isControllable()) { -+ handleJumping(); -+ return; -+ } -+ // Purpur end - Ridables - if (this.jumpDelayTicks > 0) { - --this.jumpDelayTicks; - } -@@ -469,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 - Ridables - - private final Rabbit rabbit; - private double nextJumpSpeed; -@@ -480,14 +541,14 @@ public class Rabbit extends Animal implements VariantHolder { - } - - @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.0D); - } else if (this.hasWanted() || this.operation == MoveControl.Operation.JUMPING) { - this.rabbit.setSpeedModifier(this.nextJumpSpeed); - } - -- super.tick(); -+ super.vanillaTick(); // Purpur - Ridables - } - - @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 500259e6f297276fb3d6943c2bf88c844d4ec7e4..9bc58ca4556baf6f6bc494ae249c11c5c627f86c 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,18 @@ public class Salmon extends AbstractSchoolingFish implements VariantHolder { -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 fd9f6c17448a4d87f940eb8f544ecb9669068582..0ba9aa45902cbad16ee0356cb3051b12923cfe10 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -55,12 +55,31 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - super(type, world); - } - -+ // 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, 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 - Ridables - this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Mob.class, 10, true, false, (entityliving, worldserver) -> { - return entityliving instanceof Enemy; - })); -@@ -110,6 +129,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) { -@@ -166,7 +186,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, 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 -@@ -179,7 +199,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - - return InteractionResult.SUCCESS; - } else { -- return InteractionResult.PASS; -+ return tryRide(player, hand); // Purpur - Ridables - } - } - -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 97a3f0ab3dfca24991051395229dd4c601a66fa0..2d0ecae9625e08ecf5028ec62f71a5432c462712 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -50,9 +50,32 @@ public class Squid extends AgeableWaterCreature { - this.tentacleSpeed = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F; - } - -+ // 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()); - } - -@@ -305,6 +328,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 i = this.squid.getNoActionTime(); - if (i > 100) { - this.squid.movementVector = Vec3.ZERO; -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 8d59d606bdaaea7c64389572b2810b65414a1533..df9d2d4d285f51d6e8e5bc781699c20fe1c2d00d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -+++ b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java -@@ -67,6 +67,18 @@ public class TropicalFish extends AbstractSchoolingFish implements VariantHolder - super(type, world); - } - -+ // 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 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 d6605c15111dbdb6ee61a24822bc0a9aed7198d6..0fc805540305dd8d34b903d5b7769816578c19c3 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -@@ -86,6 +86,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 pos) { - this.entityData.set(Turtle.HOME_POS, pos); - } -@@ -188,6 +205,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.2D)); - this.goalSelector.addGoal(1, new Turtle.TurtleBreedGoal(this, 1.0D)); - this.goalSelector.addGoal(1, new Turtle.TurtleLayEggGoal(this, 1.0D)); -@@ -349,13 +367,15 @@ public class Turtle extends Animal { - return this.isBaby() ? Turtle.BABY_DIMENSIONS : super.getDefaultDimensions(pose); - } - -- private static class TurtleMoveControl extends MoveControl { -+ private 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 turtle) { - super(turtle); - this.turtle = turtle; -+ waterController = new org.purpurmc.purpur.controller.WaterMoveControllerWASD(turtle, 0.25D); // Purpur - Ridables - } - - private void updateSpeed() { -@@ -375,7 +395,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 d0 = this.wantedX - this.turtle.getX(); -@@ -391,7 +411,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 c57fac6b5a17f39699298a58d9d25c12da929e64..5ac51a56c451a8fde4b98cec165143b2128fd9eb 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -@@ -124,9 +124,32 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder(this, Llama.class, 24.0F, 1.5D, 1.5D)); -@@ -138,6 +161,7 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder type, Level world) { - super(type, world); -- 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(); -@@ -121,6 +134,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(Allay.MEMORY_TYPES, Allay.SENSOR_TYPES); -@@ -228,6 +263,7 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS - ProfilerFiller gameprofilerfiller = Profiler.get(); - - gameprofilerfiller.push("allayBrain"); -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick(world, this); - gameprofilerfiller.pop(); - gameprofilerfiller.push("allayActivityUpdate"); -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 c1ef714096159608752d744b98f615cd45fe459a..4c9771725f9567790841094dae72c2bbf0d5ba62 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 -@@ -82,6 +82,23 @@ public class Armadillo extends Animal { - return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 12.0D).add(Attributes.MOVEMENT_SPEED, 0.14D); - } - -+ // 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/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..b414edf515890066bd970f65c073964839851840 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 -@@ -100,6 +100,23 @@ public class Axolotl extends Animal implements VariantHolder, B - 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 world) { - return 0.0F; -@@ -297,6 +314,7 @@ public class Axolotl extends Animal implements VariantHolder, B - ProfilerFiller gameprofilerfiller = Profiler.get(); - - gameprofilerfiller.push("axolotlBrain"); -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick(world, this); - gameprofilerfiller.pop(); - gameprofilerfiller.push("axolotlActivityUpdate"); -@@ -520,14 +538,22 @@ public class Axolotl extends Animal implements VariantHolder, B - private 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(); - } -@@ -542,9 +568,9 @@ public class Axolotl extends Animal implements VariantHolder, B - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - Ridables - if (!Axolotl.this.isPlayingDead()) { -- super.tick(); -+ super.vanillaTick(); // Purpur - Ridables - } - - } -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 f3c884ab9c09f04dd01cabf2ee9de3b5b620563d..f794ac7af227d413ed030457cbe4cd68e6eca969 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 -@@ -87,6 +87,13 @@ public class Camel extends AbstractHorse { - navigation.setCanWalkOverFences(true); - } - -+ // Purpur start - Ridables -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.camelRidableInWater; -+ } -+ // Purpur end - Ridables -+ - @Override - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); -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..b42e1e1749c9f18b3bf842517522e90ba60330ff 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 -@@ -106,6 +106,8 @@ public class Frog extends Animal implements 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 - Ridables -+ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurWaterController; // Purpur - Ridables - - public Frog(EntityType type, Level world) { - super(type, world); -@@ -113,7 +115,55 @@ public class Frog extends Animal implements VariantHolder> { - 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() { -@@ -188,6 +238,7 @@ public class Frog extends Animal implements VariantHolder> { - protected void customServerAiStep(ServerLevel world) { - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("frogBrain"); -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick(world, this); - profilerFiller.pop(); - profilerFiller.push("frogActivityUpdate"); -@@ -384,7 +435,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 - Ridables - FrogLookControl(final 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 48ac8c3f6e00c3c2dc67b6c994be7c0ac6dfcf81..87a2825d4e198e152a601f20105596a793695885 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 -@@ -51,13 +51,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 - Ridables - - public Tadpole(EntityType type, Level world) { - super(type, world); -- 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 world) { - return new WaterBoundPathNavigation(this, world); -@@ -88,6 +125,7 @@ public class Tadpole extends AbstractFish { - ProfilerFiller gameprofilerfiller = Profiler.get(); - - gameprofilerfiller.push("tadpoleBrain"); -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - 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..307d11e5ad6d0b47af36b1469a73ae1caa100232 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 -@@ -93,6 +93,23 @@ public class Goat extends Animal { - }); - } - -+ // 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(Goat.MEMORY_TYPES, Goat.SENSOR_TYPES); -@@ -197,6 +214,7 @@ public class Goat extends Animal { - ProfilerFiller gameprofilerfiller = Profiler.get(); - - gameprofilerfiller.push("goatBrain"); -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick(world, this); - gameprofilerfiller.pop(); - gameprofilerfiller.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 8aed30cdbbfdd42c20dcd4c8773c8a0ee21a980d..d9e4eb76209abffd0079ccdbbd2fc3f29bd67052 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 -@@ -228,11 +228,21 @@ 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.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 PanicGoal(this, 1.2D)); - this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2D)); - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D, AbstractHorse.class)); -@@ -243,6 +253,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 - Ridables - - 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 5cafdde956d7a5b00cd5aec5c44849639307363d..f4ef46c6cf40993b878ee965a0af397894231ba6 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 -@@ -16,6 +16,13 @@ public class Donkey extends AbstractChestedHorse { - super(type, world); - } - -+ // Purpur start - Ridables -+ @Override -+ public boolean dismountsUnderwater() { -+ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.donkeyRidableInWater; -+ } -+ // Purpur end - Ridables -+ - @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 b5ec7c8ad0e482930d1a54b590b26093f4e477ea..4505bcc6ab70ee2bc969ecfaecf7cff072f48ca1 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 -@@ -43,6 +43,13 @@ public class Horse extends AbstractHorse implements VariantHolder { - super(type, world); - } - -+ // 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((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 18bd483fe46de3d9dc129bffbccfba9d4cab9550..0e15cb99cab5ed664dc265f3754b9da7fef8958f 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 -@@ -77,7 +77,51 @@ public class Llama extends AbstractChestedHorse implements VariantHolder entitytypes, Level world) { - super(EntityType.ENDER_DRAGON, world); -@@ -129,6 +130,37 @@ public class EnderDragon extends Mob implements Enemy { - this.noPhysics = true; - this.phaseManager = new EnderDragonPhaseManager(this); - this.explosionSource = new ServerExplosion(world.getMinecraftWorld(), this, null, null, new Vec3(Double.NaN, Double.NaN, Double.NaN), Float.NaN, true, Explosion.BlockInteraction.DESTROY); // CraftBukkit -+ -+ // 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 fight) { -@@ -143,6 +175,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.0D); - } -@@ -184,6 +227,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()); -@@ -210,6 +284,8 @@ public class EnderDragon extends Mob implements Enemy { - float f; - - if (this.isDeadOrDying()) { -+ if (hasRider) ejectPassengers(); // Purpur - Ridables -+ - float f1 = (this.random.nextFloat() - 0.5F) * 8.0F; - - f = (this.random.nextFloat() - 0.5F) * 4.0F; -@@ -222,9 +298,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 - Ridables - this.flapTime += 0.1F; -- } else if (this.inWall) { -+ } else if (!hasRider && this.inWall) { // Purpur - Ridables - this.flapTime += f * 0.5F; - } else { - this.flapTime += f; -@@ -240,7 +316,7 @@ public class EnderDragon extends Mob implements Enemy { - float f4; - float f5; - -- if (world1 instanceof ServerLevel) { -+ if (world1 instanceof ServerLevel && !hasRider) { // Purpur - Ridables - ServerLevel worldserver1 = (ServerLevel) world1; - DragonPhaseInstance idragoncontroller = this.phaseManager.getCurrentPhase(); - -@@ -326,7 +402,7 @@ public class EnderDragon extends Mob implements Enemy { - if (world2 instanceof ServerLevel) { - ServerLevel worldserver2 = (ServerLevel) world2; - -- if (this.hurtTime == 0) { -+ if (!hasRider && this.hurtTime == 0) { // Purpur - Ridables - this.knockBack(worldserver2, worldserver2.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(worldserver2, worldserver2.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(worldserver2, worldserver2.getEntities((Entity) this, this.head.getBoundingBox().inflate(1.0D), EntitySelector.NO_CREATIVE_OR_SPECTATOR)); -@@ -374,7 +450,7 @@ public class EnderDragon extends Mob implements Enemy { - if (world3 instanceof ServerLevel) { - ServerLevel worldserver3 = (ServerLevel) world3; - -- this.inWall = this.checkWalls(worldserver3, this.head.getBoundingBox()) | this.checkWalls(worldserver3, this.neck.getBoundingBox()) | this.checkWalls(worldserver3, this.body.getBoundingBox()); -+ this.inWall = !hasRider && this.checkWalls(worldserver3, this.head.getBoundingBox()) | this.checkWalls(worldserver3, this.neck.getBoundingBox()) | this.checkWalls(worldserver3, this.body.getBoundingBox()); // Purpur - Ridables - 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 bd9e10f79eaf0d23908229b3ebc2227946a14843..236e8ea79119dcf21c066b7c250fe20e6752df9e 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 -@@ -86,16 +86,30 @@ public class WitherBoss extends Monster implements RangedAttackMob { - return !entityliving.getType().is(EntityTypeTags.WITHER_FRIENDS) && entityliving.attackable(); - }; - private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0D).selector(WitherBoss.LIVING_ENTITY_SELECTOR); -+ private int shootCooldown = 0; // Purpur - Ridables - // Paper start - private boolean canPortal = false; - - public void setCanTravelThroughPortals(boolean canPortal) { this.canPortal = canPortal; } - // Paper end -+ private org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD purpurController; // Purpur - Ridables - - 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 - 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.setHealth(this.getMaxHealth()); - this.xpReward = 50; - } -@@ -109,13 +123,114 @@ public class WitherBoss extends Monster implements RangedAttackMob { - return navigationflying; - } - -+ // 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 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.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 - Ridables - 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)); - } -@@ -260,6 +375,15 @@ public class WitherBoss extends Monster implements RangedAttackMob { - - @Override - protected void customServerAiStep(ServerLevel world) { -+ // 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 - int i; - - if (this.getInvulnerableTicks() > 0) { -@@ -578,11 +702,11 @@ public class WitherBoss extends Monster implements 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 - Ridables - } - - 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 - Ridables - } - - public boolean isPowered() { -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 90b6ed81dcfd4021c7e9509da5e8725034fa07e5..22b003a23b519bedc50bbcad0706aa2d7d7f4b3a 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -@@ -74,12 +74,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.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 - Ridables - 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 e33fa82ca1332b95bb067fd621212d3026eee1b7..968af87ceeb1862738c5270c2aad85c7974cfdb3 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Blaze.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Blaze.java -@@ -33,6 +33,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 - Ridables - this.setPathfindingMalus(PathType.WATER, -1.0F); - this.setPathfindingMalus(PathType.LAVA, 8.0F); - this.setPathfindingMalus(PathType.DANGER_FIRE, 0.0F); -@@ -40,19 +41,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 -@@ -117,6 +154,13 @@ public class Blaze extends Monster { - - @Override - protected void customServerAiStep(ServerLevel world) { -+ // 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/src/main/java/net/minecraft/world/entity/monster/Bogged.java b/src/main/java/net/minecraft/world/entity/monster/Bogged.java -index 975477663b6d76a69c006a89e440e21471b39b89..05848ad509461f3fd40d820098cf7cbfe6866b0c 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Bogged.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Bogged.java -@@ -44,6 +44,23 @@ public class Bogged extends AbstractSkeleton implements Shearable { - super(type, world); - } - -+ // 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/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -index 4e621a7f36b3d718695434a2a4e3060283667bb2..a21a18e249dbfa0f1d3d6995b6810961ed5e084b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -@@ -27,6 +27,23 @@ public class CaveSpider extends Spider { - return Spider.createAttributes().add(Attributes.MAX_HEALTH, 12.0D); - } - -+ // 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 world, Entity target) { - if (super.doHurtTarget(world, 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 1906dfc22af208d6e801ad4a8f2f9e9702432691..ff7ee3a07f0c84aaa36be3a324274b4ccb04fb0b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -60,21 +60,98 @@ public class Creeper extends Monster { - public int explosionRadius = 3; - private int droppedSkulls; - public Entity entityIgniter; // CraftBukkit -+ // Purpur start - Ridables -+ private int spacebarCharge = 0; -+ private int prevSpacebarCharge = 0; -+ private int powerToggleDelay = 0; -+ // Purpur end - Ridables - - public Creeper(EntityType type, Level world) { - super(type, world); - } - -+ // 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.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 - Ridables - 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 { - 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 - Ridables - } - } - // Paper end - CreeperIgniteEvent -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 c6c86913c0a48501a9109a3838a3e56685d16d79..025133dff77df06e147defb70f920ee140b9881f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -75,6 +75,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 void addBehaviourGoals() { - this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0)); -@@ -398,7 +415,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 drowned) { -@@ -407,7 +424,7 @@ public class Drowned extends Zombie implements RangedAttackMob { - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - Ridables - LivingEntity livingEntity = this.drowned.getTarget(); - if (this.drowned.wantsToSwim() && this.drowned.isInWater()) { - if (livingEntity != null && livingEntity.getY() > this.drowned.getY() || this.drowned.searchingForLand) { -@@ -427,7 +444,7 @@ public class Drowned extends Zombie implements RangedAttackMob { - float h = (float)(Mth.atan2(f, d) * 180.0F / (float)Math.PI) - 90.0F; - this.drowned.setYRot(this.rotlerp(this.drowned.getYRot(), h, 90.0F)); - this.drowned.yBodyRot = this.drowned.getYRot(); -- float i = (float)(this.speedModifier * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED)); -+ float i = (float)(this.getSpeedModifier() * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED)); // Purpur - Ridables - float j = Mth.lerp(0.125F, this.drowned.getSpeed(), i); - this.drowned.setSpeed(j); - this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add((double)j * d * 0.005, (double)j * e * 0.1, (double)j * f * 0.005)); -@@ -436,7 +453,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/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java -index 378694a38115c012978e1fea59d049d1ebd04110..bff963a1a0d89b57a686ed06aa630e789a602d82 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 - 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.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 2a394381a4ad46359359ba402b65c62b331480b4..f3013664d21f70c2650306f2406e93b21de8f9e0 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -94,9 +94,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.0D, false)); - this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D, 0.0F)); -@@ -104,6 +122,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, new Class[0])); - this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, true, false)); -@@ -267,7 +286,7 @@ public class EnderMan extends Monster implements NeutralMob { - - @Override - protected void customServerAiStep(ServerLevel world) { -- if (world.isDay() && this.tickCount >= this.targetChangeTime + 600) { -+ if ((getRider() == null || !this.isControllable()) && world.isDay() && this.tickCount >= this.targetChangeTime + 600) { // Purpur - no random teleporting - float f = this.getLightLevelDependentMagicValue(); - - if (f > 0.5F && world.canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper - EndermanEscapeEvent -@@ -382,6 +401,7 @@ public class EnderMan extends Monster implements NeutralMob { - public boolean hurtServer(ServerLevel world, DamageSource source, float amount) { - if (this.isInvulnerableTo(world, source)) { - return false; -+ } else if (getRider() != null && this.isControllable()) { return super.hurtServer(world, 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 534e98dd7291e09dee1d0f77cbf221b15708590f..ac9116735d8ee4713fab8d90ef12e781b77b26c8 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Endermite.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Endermite.java -@@ -38,14 +38,33 @@ public class Endermite extends Monster { - this.xpReward = 3; - } - -+ // 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.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 - Ridables - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).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 14f23c9a248760a57b3d6fe4f2824a4a456a6d37..69a37f88c7f05084007cc2f895c31650da6566f8 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Evoker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Evoker.java -@@ -53,10 +53,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.6D, 1.0D)); - this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 0.6D, 1.0D)); -@@ -66,6 +84,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 - Ridables - 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 a8c8c03e972aa6352843cf4c3e4aebfb8f493125..8cb7bdd96f9780ee5e2fa0876bc80d092e06b5f5 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 - 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; -+ } -+ -+ @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(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, (entityliving, worldserver) -> { - 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 - Ridables - } - - @Override -@@ -155,7 +191,7 @@ public class Ghast extends FlyingMob implements Enemy { - - } - -- private static class GhastMoveControl extends MoveControl { -+ private static class GhastMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - Ridables - - private final Ghast ghast; - private int floatDuration; -@@ -166,7 +202,7 @@ public class Ghast extends FlyingMob implements Enemy { - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - Ridables - 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 118521ae54254b0a73bb7cba7b2871c9c26f89fc..64e4f78fd626c59e2957140af22993aa8e5c4f15 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,29 @@ public class Giant extends Monster { - super(type, world); - } - -+ // 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).add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.ATTACK_DAMAGE, 50.0); - } -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 951f46684623582980901c1ebc1870aa5bcf25a1..bde5d88dcdb4f2305e2786842551c36da5ed9778 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Guardian.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Guardian.java -@@ -67,15 +67,36 @@ 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 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 - Ridables - this.goalSelector.addGoal(4, this.guardianAttackGoal = new Guardian.GuardianAttackGoal(this)); // CraftBukkit - assign field - this.goalSelector.addGoal(5, pathfindergoalmovetowardsrestriction); - this.goalSelector.addGoal(7, this.randomStrollGoal); -@@ -84,6 +105,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 - Ridables - this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 10, true, false, new Guardian.GuardianAttackSelector(this))); - } - -@@ -330,7 +352,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 - Ridables - this.move(MoverType.SELF, this.getDeltaMovement()); - this.setDeltaMovement(this.getDeltaMovement().scale(0.9D)); - if (!this.isMoving() && this.getTarget() == null) { -@@ -342,7 +364,7 @@ public class Guardian extends Monster { - - } - -- private static class GuardianMoveControl extends MoveControl { -+ private static class GuardianMoveControl extends org.purpurmc.purpur.controller.WaterMoveControllerWASD { // Purpur - Ridables - - private final Guardian guardian; - -@@ -351,8 +373,17 @@ public class Guardian extends Monster { - this.guardian = guardian; - } - -+ // 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 vec3d = new Vec3(this.wantedX - this.guardian.getX(), this.wantedY - this.guardian.getY(), this.wantedZ - this.guardian.getZ()); - double d0 = vec3d.length(); -@@ -363,7 +394,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 - Ridables - 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 184fa759db065fb345f3623752229430816d8ad3..f396652fdc30ec5638b30a8b4189f05fb0eee12d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Husk.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java -@@ -23,6 +23,23 @@ public class Husk extends Zombie { - super(type, world); - } - -+ // 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 -+ - public static boolean checkHuskSpawnRules(EntityType type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) { - return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (EntitySpawnReason.isSpawner(spawnReason) || 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 db3aac9ba711dcd18ffc35c4a745ecaec89d0166..8ff616fe05071bd2a197a465c7e17f35a3e72d44 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 - 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.0D, 1.2D)); - 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.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 - Ridables - 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 ae710c3fffc7840a9ff2cbc5cdacef8a2e248253..4f7d99fadfa1ba31439ec02bfb107288a722e828 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -@@ -24,6 +24,28 @@ public class MagmaCube extends Slime { - super(type, world); - } - -+ // 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); - } -@@ -71,6 +93,7 @@ public class MagmaCube extends Slime { - float f = (float)this.getSize() * 0.1F; - this.setDeltaMovement(vec3.x, (double)(this.getJumpPower() + f), vec3.z); - this.hasImpulse = true; -+ this.actualJump = false; // Purpur - Ridables - } - - @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 4ff75412452649ebf106ef591cb97dc7ac8175e7..afd00fd83e6c246afecf7042435ae119057b9e93 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 - 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; -+ } -+ -+ @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 - Ridables -+ - @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 - 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()); - } - -@@ -140,6 +200,7 @@ public class Phantom extends FlyingMob implements Enemy { - @Override - public void aiStep() { - if (this.isAlive() && this.shouldBurnInDay && this.isSunBurnTick()) { // Paper - shouldBurnInDay API -+ if (getRider() == null || !this.isControllable()) // Purpur - Ridables - this.igniteForSeconds(8.0F); - } - -@@ -254,7 +315,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 - Ridables - - private float speed = 0.1F; - -@@ -262,8 +323,19 @@ public class Phantom extends FlyingMob implements Enemy { - super(entity); - } - -+ // 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; -@@ -309,14 +381,20 @@ public class Phantom extends FlyingMob implements Enemy { - } - } - -- private static class PhantomLookControl extends LookControl { -+ private static class PhantomLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables - - public PhantomLookControl(Mob entity) { - super(entity); - } - -+ // 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 - } - - 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 91edf8767541982b8cd1be83c33a7b287ffb62fe..a629e31e6ea0f49f88746856382fcec96918c490 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -@@ -67,16 +67,35 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve - super(type, world); - } - -+ // 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.0D, 1.2D)); - 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 - Ridables - this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{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 cfc28828a5b81563a826ae6045553e7350f67986..6222ed8408096e0bb8e9572c07c0db6971fc8308 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -75,14 +75,39 @@ 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)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables - this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, true)); - 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 - Ridables - 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, worldserver) -> { -@@ -135,7 +160,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.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 64d99b8b576212f754bd316343562b1ba7f604fa..6541e1059ca16cfd01bf01aae2c56400cbe78132 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 3972e2ed0554e2550519e994888e068df0a151e5..78c8483da3c0ac7f93e236dd723fc6051427a50e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -@@ -29,6 +29,23 @@ public class Skeleton extends AbstractSkeleton { - super(type, world); - } - -+ // 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/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java -index 72346a7e5269c91e3143933ac37e65ad9639b791..b7941230b082d4de9ab77c981bd396fa1184d78e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Slime.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java -@@ -65,6 +65,7 @@ public class Slime extends Mob implements Enemy { - public float squish; - public float oSquish; - private boolean wasOnGround; -+ protected boolean actualJump; // Purpur - Ridables - - public Slime(EntityType type, Level world) { - super(type, world); -@@ -72,12 +73,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, (entityliving, worldserver) -> { - return Math.abs(entityliving.getY() - this.getY()) <= 4.0D; - })); -@@ -386,6 +423,7 @@ public class Slime extends Mob implements Enemy { - - this.setDeltaMovement(vec3d.x, (double) this.getJumpPower(), vec3d.z); - this.hasImpulse = true; -+ this.actualJump = false; // Purpur - Ridables - } - - @Nullable -@@ -419,7 +457,7 @@ public class Slime extends Mob implements Enemy { - return super.getDefaultDimensions(pose).scale((float) this.getSize()); - } - -- private static class SlimeMoveControl extends MoveControl { -+ private static class SlimeMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables - - private float yRot; - private int jumpDelay; -@@ -438,21 +476,33 @@ public class Slime extends Mob implements Enemy { - } - - public void setWantedMovement(double speed) { -- this.speedModifier = speed; -+ this.setSpeedModifier(speed); // 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) { -@@ -469,7 +519,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/src/main/java/net/minecraft/world/entity/monster/Spider.java b/src/main/java/net/minecraft/world/entity/monster/Spider.java -index 91e521414c3ea5722aac7506b7589fbb399e9636..d3eabdde9e9bf010cae7fc81165f0123adfcf958 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Spider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java -@@ -51,9 +51,27 @@ public class Spider extends Monster { - super(type, world); - } - -+ // 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.0D, 1.2D, (entityliving) -> { - return !((Armadillo) entityliving).isScared(); - })); -@@ -62,6 +80,7 @@ public class Spider extends Monster { - 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 - Ridables - 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 baaf17107584b253d7e268749849bf5b0d0c88ab..879748708e3fc8c0a3f126d265e99a7c054d2a10 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Stray.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Stray.java -@@ -22,6 +22,23 @@ public class Stray extends AbstractSkeleton { - super(type, world); - } - -+ // 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 type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random - ) { -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 0a9246241985d2d97beb865b7163f1d2198f03b8..7b1525c6bc46d65660588d90c3121ad3d12cf077 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Strider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java -@@ -97,6 +97,23 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - 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 type, LevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) { - BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable(); - -@@ -158,6 +175,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - @Override - protected void registerGoals() { - this.goalSelector.addGoal(1, new PanicGoal(this, 1.65D)); -+ this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables - this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); - this.temptGoal = new TemptGoal(this, 1.4D, (itemstack) -> { - return itemstack.is(ItemTags.STRIDER_TEMPT_ITEMS); -@@ -466,7 +484,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - if (!enuminteractionresult.consumesAction()) { - ItemStack itemstack = player.getItemInHand(hand); - -- return (InteractionResult) (itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : InteractionResult.PASS); -+ return (InteractionResult) (itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : tryRide(player, hand)); // Purpur - Ridables - } 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 183a33b7d666d652b455baa7e8339e9c4a870a58..fba52457f85573f5918aeeb5f3b69b3f113cc9d5 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vex.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java -@@ -59,6 +59,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(float fallDistance, float damageMultiplier, DamageSource damageSource) { -+ return false; // no fall damage please -+ } -+ // Purpur end - Ridables -+ - @Override - public boolean isFlapping() { - return this.tickCount % Vex.TICKS_PER_FLAP == 0; -@@ -71,7 +115,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); -@@ -86,17 +130,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, 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 -@@ -228,14 +274,14 @@ public class Vex extends Monster implements TraceableEntity { - this.setDropChance(EquipmentSlot.MAINHAND, 0.0F); - } - -- private class VexMoveControl extends MoveControl { -+ private class VexMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - Ridables - - public VexMoveControl(final Vex entityvex) { - super(entityvex); - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - Ridables - 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(); -@@ -244,7 +290,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 - Ridables - 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 96b105697c91314148fd1b783501389214b1a3f0..5bf2aad976be5d6149b8252c84cd870551a2aa8e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -@@ -55,15 +55,34 @@ public class Vindicator extends AbstractIllager { - super(type, world); - } - -+ // 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/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java -index a03fa8a3e648532a7ffaaf523ca87c13e8af4c0a..5cba860f9ce81d90eec4c6bf45699d28cf8d93e6 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 - 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, (TargetingConditions.Selector) 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.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 - Ridables - 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 557b4e225688416132281e9b1759d46a9b775ff9..626cab5a974d2c8736123cc23e535b5cf0e5349e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -@@ -36,6 +36,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/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index 35b0c5c322864e2f5ae5a412296072f268adcd05..2ac14783e7b5739a13c487d5028ecba38480980d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -@@ -85,6 +85,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); -@@ -250,6 +267,7 @@ public class Zoglin extends Monster implements HoglinBase { - protected void customServerAiStep(ServerLevel world) { - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("zoglinBrain"); -+ if (getRider() == null || !this.isControllable()) // Purpur - only use brain if no rider - this.getBrain().tick(world, this); - profilerFiller.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 a12461907278cfbfa3b1c0aa74b9f07a31768b8a..9b4b923117a7025bdbb6d222c6388aeae9bef8a2 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -110,11 +110,30 @@ public class Zombie extends Monster { - this(EntityType.ZOMBIE, world); - } - -+ // 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.0D, 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/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index 30bce56a70f923b0ec77c8e3f29e435a71c71510..9fafe05ef0ffc1120873727082290a8ea177d62f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -85,6 +85,23 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - }); - } - -+ // 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/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index 03e3cbe73119ca76417d4dd192e1560bdfc373ec..69c291c3347a3e3f454ecb8f418a310bbd688a43 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(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 - public void setPersistentAngerTarget(@Nullable UUID angryAt) { - this.persistentAngerTarget = angryAt; -diff --git a/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java b/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java -index 6a7e725edece3043c8523d387e2929d5ba8932cb..6716bfa903be5ab34b80c963cc9d6a8a26272621 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java -+++ b/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java -@@ -106,6 +106,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); -@@ -575,31 +598,31 @@ public class Creaking extends Monster { - return 0.0F; - } - -- private class CreakingLookControl extends LookControl { -+ private class CreakingLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables - - public CreakingLookControl(final Creaking creaking) { - super(creaking); - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - Ridables - if (Creaking.this.canMove()) { -- super.tick(); -+ super.vanillaTick(); // Purpur - Ridables - } - - } - } - -- private class CreakingMoveControl extends MoveControl { -+ private class CreakingMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables - - public CreakingMoveControl(final Creaking creaking) { - super(creaking); - } - - @Override -- public void tick() { -+ public void vanillaTick() { // Purpur - Ridables - if (Creaking.this.canMove()) { -- super.tick(); -+ super.vanillaTick(); // Purpur - Ridables - } - - } -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..4ab971e86b48ce3010928fe9046e8f68224719ca 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 -@@ -71,6 +71,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; -@@ -143,6 +160,7 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - ProfilerFiller gameprofilerfiller = Profiler.get(); - - gameprofilerfiller.push("hoglinBrain"); -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - 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..b19e3b442a650f773df462e32088648a2b7eafd4 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 -@@ -99,6 +99,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 - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); -@@ -312,6 +329,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - ProfilerFiller gameprofilerfiller = Profiler.get(); - - gameprofilerfiller.push("piglinBrain"); -+ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider - this.getBrain().tick(world, this); - gameprofilerfiller.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 24eaeb93284fe1a573026b85818a93a34fd9e1ec..3dc234f8cea8769f715a4913ae4ecf7d47433577 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 -@@ -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) -@@ -117,6 +134,7 @@ public class PiglinBrute extends AbstractPiglin { - protected void customServerAiStep(ServerLevel world) { - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("piglinBruteBrain"); -+ if (getRider() == null || this.isControllable()) // Purpur - only use brain if no rider - this.getBrain().tick(world, this); - profilerFiller.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 c47ed605f0822effd58df4f875297ed015e1e57e..19f7a6d55144adb5217fbea590d8f23d79ed05e0 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 -@@ -127,8 +127,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 entityTrackerEntry) { - return new ClientboundAddEntityPacket(this, entityTrackerEntry, this.hasPose(Pose.EMERGING) ? 1 : 0); -@@ -396,17 +420,14 @@ 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 - Ridables - if (entity instanceof LivingEntity entityliving) { - 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 2d8ba55906c8da16fde850e3412f4a6bda3d56e7..066836e450ca8d104990c3c5fba5fe3dad2e136e 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 - 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(); // CraftBukkit - decompile error -@@ -255,7 +277,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 && (getRider() == null || !this.isControllable()) /*&& this.behaviorTick++ % this.activatedPriority == 0*/) { -+ this.getBrain().tick(world, this); // Paper // Purpur - Ridables -+ } -+ // Pufferfish end - gameprofilerfiller.pop(); - if (this.assignProfessionWhenSpawned) { - this.assignProfessionWhenSpawned = false; -@@ -312,7 +338,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.SUCCESS; -+ return tryRide(player, hand, InteractionResult.SUCCESS); // Purpur - Ridables - } else { - if (!this.level().isClientSide) { - boolean flag = this.getOffers().isEmpty(); -@@ -326,9 +352,10 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - } - - if (flag) { -- return InteractionResult.CONSUME; -+ return tryRide(player, hand, InteractionResult.CONSUME); // Purpur - Ridables - } - -+ if (level().purpurConfig.villagerRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur - Ridables - 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 1e77cce428d9e53142aaa2cf780b7f862d536eca..c5bef9f0ad2a0f57d8c37ea0833e899dc588d30f 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -72,6 +72,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 - Ridables -+ - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new FloatGoal(this)); -@@ -120,9 +137,9 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - - if (!this.level().isClientSide) { - if (this.getOffers().isEmpty()) { -- return InteractionResult.CONSUME; -+ return tryRide(player, hand, InteractionResult.CONSUME); // Purpur - Ridables - } -- -+ if (level().purpurConfig.wanderingTraderRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur - Ridables - 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 5b8b85a295a08ae495f729c595b3a78778965342..28a4cf814ec4b34dce883ba4f24ca55008c8f6c1 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -211,6 +211,19 @@ public abstract class Player extends LivingEntity { - } - // 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 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 958ea103cc80da7366cc33dc385b76d4f5c809f2..f8ff53488d886bfd67ca3bfe4431b42010052d87 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/LlamaSpit.java -@@ -33,6 +33,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 - Ridables -+ public void super_tick() { -+ super.tick(); -+ } -+ // Purpur end - Ridables -+ - @Override - protected double getDefaultGravity() { - return 0.06D; -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 4c47b30867e30d84908abf93dbefc252bc8c3453..8296765d8f63f1a9fd207b27d495d7c04646f134 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,14 @@ public class WitherSkull extends AbstractHurtingProjectile { - - } - -+ // 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(WitherSkull.DATA_DANGEROUS, false); -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index b25b10c24a379097233e61bcc10add841b6a7115..c105d0cce462df46e106eb502355225b83be32b7 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -1306,4 +1306,27 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - } - } - // Paper end - broadcast hurt animation -+ -+ // 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 e37aaf77f94b97b736cc20ef070cefdff0400188..eb2f9bfdaf3ed8a684337a15365e70174d1533b3 100644 ---- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -@@ -602,6 +602,15 @@ public class CraftEventFactory { - // Paper end - craftServer.getPluginManager().callEvent(event); - -+ // Purpur start - Ridables -+ 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 - Ridables -+ - return event; - } - -@@ -1191,6 +1200,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/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index c2991c34fd4306fae79fca2c1349c826b3247c49..e8c9393760108f2549b52a61d973ea2b4a64a312 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -175,4 +175,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 42e502cfcb8d2e775cbf738773caf1a087d2f3f4..5004e86747306cc8d4bbed6f10d3a6e9047cb5f4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -90,4 +90,753 @@ 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 armadilloRidable = false; -+ public boolean armadilloRidableInWater = true; -+ public boolean armadilloControllable = true; -+ private void armadilloSettings() { -+ armadilloRidable = getBoolean("mobs.armadillo.ridable", armadilloRidable); -+ armadilloRidableInWater = getBoolean("mobs.armadillo.ridable-in-water", armadilloRidableInWater); -+ armadilloControllable = getBoolean("mobs.armadillo.controllable", armadilloControllable); -+ } -+ -+ 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 boggedRidable = false; -+ public boolean boggedRidableInWater = true; -+ public boolean boggedControllable = true; -+ private void boggedSettings() { -+ boggedRidable = getBoolean("mobs.bogged.ridable", boggedRidable); -+ boggedRidableInWater = getBoolean("mobs.bogged.ridable-in-water", boggedRidableInWater); -+ boggedControllable = getBoolean("mobs.bogged.controllable", boggedControllable); -+ } -+ -+ 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 creakingRidable = false; -+ public boolean creakingRidableInWater = true; -+ public boolean creakingControllable = true; -+ private void creakingSettings() { -+ creakingRidable = getBoolean("mobs.creaking.ridable", creakingRidable); -+ creakingRidableInWater = getBoolean("mobs.creaking.ridable-in-water", creakingRidableInWater); -+ creakingControllable = getBoolean("mobs.creaking.controllable", creakingControllable); -+ } -+ -+ 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 skeletonHorseRidable = false; -+ public boolean skeletonHorseRidableInWater = true; -+ public boolean skeletonHorseCanSwim = 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); -+ } -+ -+ 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 zombieHorseRidable = false; -+ public boolean zombieHorseRidableInWater = false; -+ public boolean zombieHorseCanSwim = 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); -+ } -+ -+ 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..940bcc6f79b59cb3cce578912eb789efd394f456 ---- /dev/null -+++ b/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/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..e0bbaec05afa0ae67ed486b14ea1fbadbbe90d9b ---- /dev/null -+++ b/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/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..dd219518150ca90f89ad238904fd4095efe032d8 ---- /dev/null -+++ b/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().broadcast(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/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..34f3c43fa16e950326ac5e3d93faee0466ffedc6 ---- /dev/null -+++ b/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/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..922e48799c43ca322a8f550c98a26e1e2959439c ---- /dev/null -+++ b/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/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..e33e54fc31ab7dcff054d0ab245d6c3391d06449 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java -@@ -0,0 +1,101 @@ -+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; -+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); -+ 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.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, -+ false, true, -+ 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/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..3759e45afe16bf1d8a37b78d3526ee446e63cfe5 ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/PhantomFlames.java -@@ -0,0 +1,123 @@ -+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.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, -+ false, true, -+ 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.isInWaterOrBubble()) { -+ 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/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/patches/server/0008-Configurable-entity-base-attributes.patch b/patches/server/0008-Configurable-entity-base-attributes.patch deleted file mode 100644 index f3e6124c05..0000000000 --- a/patches/server/0008-Configurable-entity-base-attributes.patch +++ /dev/null @@ -1,3253 +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 5c14180d92e1baebe59b08311746418e7d9f6a24..755861185bd8434027acca7f03ed0bfdf9fa2cde 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -177,7 +177,7 @@ import org.bukkit.plugin.PluginManager; - // CraftBukkit end - - public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess, ScoreHolder, 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; - public boolean preserveMotion = true; // Paper - Fix Entity Teleportation and cancel velocity if teleported; 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 293ffe990de70f4f8872f063388a3a50c60b68e6..66a5c485ed2d29d0079ae714c2dd7b01aab11d86 100644 ---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java -+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java -@@ -38,6 +38,12 @@ 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/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 715b76bd0ccc0c29583a55f82a8ecd889ab49b56..6884475a5217acb8073dc38e5f7a7b341c8b1dd1 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -324,6 +324,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.activeLocationDependentEnchantments = new EnumMap(EquipmentSlot.class); - this.appliedScale = 1.0F; - this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type), this); // Purpur - Ridables -+ this.initAttributes(); // Purpur - Configurable entity base attributes - 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()); -@@ -338,6 +339,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 - Configurable entity base attributes -+ - 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 add1c146cd7428547d9ef8810841b4cf39a6a05e..5f11c687df87015261d1d39c957e241fbeb5476a 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -100,6 +100,20 @@ 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() && (float) this.tickCount % 10.0F == 0.0F; -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 13f6e4c83e1775daadb13e3532d7dfe6eef15aac..8751cc882f2dcbf6dfc10cebab9d9a4f95ebfb10 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -489,6 +489,13 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - return tileentitybeehive != null && tileentitybeehive.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 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 472bbf4c3f932e2b1c7d7fa3c74b41f5be11431f..b4f022093a52c1fe13ad67ad70d57fd0278a9d55 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -125,6 +125,13 @@ public class Cat extends TamableAnimal implements VariantHolder { -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 5eaa1d42ec72ff077d974db3284cae32a9809da7..2f1518536e63dfd94db5c8a2076004319408409c 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -70,6 +70,13 @@ 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/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java -index fcf7073dd2d79f1483bdc6e7fdc37c8c260ae418..dad2dc77afead53e0fa7f2f797ac3850279d5d40 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,12 @@ 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/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index dc7ccfe90a82892d65098a325fd71fbbc734da86..064188a7032170ed16cf3b538efc444e54325036 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -61,6 +61,13 @@ public class Cow extends Animal { - } - // 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 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 af677b6581514a07e6455977ffc591538d43bbc6..2ee7de39712d67b593ff287a9ed17c28fa768b3c 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -161,6 +161,13 @@ 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 - @Nullable - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData entityData) { -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 ce5ac300582f61d0f3eeb1e94340cfefbdff1ba9..584d08bca961f8b8487b844cd2412b773401296d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -182,6 +182,13 @@ public class Fox extends Animal implements VariantHolder { - } - // 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/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index 938a0c6f7cfbb6cd459d5a2ec46f912d45fd2226..124b7d6881964039829313c52427e332e1ac526b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -79,6 +79,13 @@ 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 (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur - Ridables -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 f37c8efa34efcb289bbeed06ea2d3860ff2662ac..215ec83077a1d40fdcee5bf4cd1af0d46fdd695e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -81,6 +81,12 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { - } - // 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/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -index 9bc58ca4556baf6f6bc494ae249c11c5c627f86c..88f0f9074db9a9afff55aa6bd17c38fa2e1e1f81 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -@@ -47,6 +47,12 @@ public class Salmon extends AbstractSchoolingFish implements VariantHolder brainProvider() { - return Brain.provider(Allay.MEMORY_TYPES, Allay.SENSOR_TYPES); -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 4c9771725f9567790841094dae72c2bbf0d5ba62..1a6c88558a11066ec2a78d40e6a1b0f2fa546b88 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 -@@ -99,6 +99,13 @@ 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/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index b414edf515890066bd970f65c073964839851840..f95b9be60c3725fe279a07300f706abb25e152f1 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 -@@ -117,6 +117,13 @@ public class Axolotl extends Animal implements VariantHolder, B - } - // 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 world) { - return 0.0F; -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 f794ac7af227d413ed030457cbe4cd68e6eca969..ab0b90c300d0610e423abe7ac9e5b93305a21c5a 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 -@@ -321,6 +321,22 @@ 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 - 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 d9e4eb76209abffd0079ccdbbd2fc3f29bd67052..f58a0f50d04004587d342c1bb5f681cd485cf302 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 -@@ -240,6 +240,45 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, - } - // 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.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; -+ } -+ // Purpur end - Configurable entity base attributes - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HorseHasRider(this)); // Purpur - Ridables -@@ -1280,7 +1319,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); - } - -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 f4ef46c6cf40993b878ee965a0af397894231ba6..8fd709bcd4c7a5a875bdc65fd4dd1420ea618e3a 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 -@@ -23,6 +23,22 @@ 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 - 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 4505bcc6ab70ee2bc969ecfaecf7cff072f48ca1..13056a0a13eeb3dcc164344b973e6ff656c0793d 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 -@@ -50,6 +50,22 @@ public class Horse extends AbstractHorse implements VariantHolder { - } - // 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((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 0e15cb99cab5ed664dc265f3754b9da7fef8958f..83fdbf55384a5c4429d65a88fcb788e449a8862a 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 -@@ -123,6 +123,22 @@ public class Llama extends AbstractChestedHorse implements VariantHolder type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) { - return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (EntitySpawnReason.isSpawner(spawnReason) || 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 8ff616fe05071bd2a197a465c7e17f35a3e72d44..6e3362324c81afacaaa0f9766e2d23a0a4f53ac3 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,15 @@ 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/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -index 4f7d99fadfa1ba31439ec02bfb107288a722e828..5ba64f9c653345b742624f18c4cf692d1e7880e1 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -@@ -46,6 +46,27 @@ 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/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index afd00fd83e6c246afecf7042435ae119057b9e93..ee00c4c7a1e0f08cdeccab63a20c4b465fdeb549 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 - 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() { -@@ -175,6 +178,22 @@ public class Phantom extends FlyingMob implements Enemy { - return true; - } - -+ // 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/src/main/java/net/minecraft/world/entity/monster/Pillager.java b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -index a629e31e6ea0f49f88746856382fcec96918c490..78c01f0be0cc6689f68e2fcf3111d79abb5a59fb 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,13 @@ 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/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -index 6222ed8408096e0bb8e9572c07c0db6971fc8308..ecf37a99fa5e8919146ba73c7313998855ea0d88 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -98,6 +98,13 @@ 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/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -index 6541e1059ca16cfd01bf01aae2c56400cbe78132..c04d6a5190f3db06601c3a0ab0ddaede3a9f88ac 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -84,7 +84,7 @@ public class Shulker extends AbstractGolem implements VariantHolder public - Configurable entity base attributes - private float currentPeekAmountO; - private float currentPeekAmount; - @Nullable -@@ -115,6 +115,12 @@ public class Shulker extends AbstractGolem implements VariantHolder variant) { -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 6f1d59651552ef7443eb8274765614d5abc984df..191724aa48081017adf3db0b6ff99a77dd4ce68d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java -@@ -61,6 +61,15 @@ 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/src/main/java/net/minecraft/world/entity/monster/Skeleton.java b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -index 78c8483da3c0ac7f93e236dd723fc6051427a50e..8e93da5112cbb14af1ca755c40698740303717ee 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -@@ -46,6 +46,12 @@ 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/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java -index b7941230b082d4de9ab77c981bd396fa1184d78e..d2cdb2119cd6e0afd57e00b1b706d36342b8858d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Slime.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java -@@ -107,6 +107,38 @@ 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 -@@ -139,9 +171,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 - Configurable entity base attributes - 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 - Configurable entity base attributes - 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 d3eabdde9e9bf010cae7fc81165f0123adfcf958..26ce5425d8217f8954ceef898b806d3b56c01027 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,13 @@ 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/src/main/java/net/minecraft/world/entity/monster/Stray.java b/src/main/java/net/minecraft/world/entity/monster/Stray.java -index 879748708e3fc8c0a3f126d265e99a7c054d2a10..4b4eced856ad68c38042b5368bf1b9433ec1e8b1 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Stray.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Stray.java -@@ -39,6 +39,12 @@ 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 type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random - ) { -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 7b1525c6bc46d65660588d90c3121ad3d12cf077..c66126f3a43e6e011e5f9a977ad481c96529d1d5 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Strider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java -@@ -114,6 +114,13 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - } - // 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 type, LevelAccessor world, EntitySpawnReason 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 fba52457f85573f5918aeeb5f3b69b3f113cc9d5..906934076dd721a39f8ee960fc7c0d1058b66ae2 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vex.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java -@@ -103,6 +103,13 @@ 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 % Vex.TICKS_PER_FLAP == 0; -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 5bf2aad976be5d6149b8252c84cd870551a2aa8e..065bd31afe948c1ffab4da6f2a236cd931e75259 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -@@ -72,6 +72,13 @@ 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/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java -index 5cba860f9ce81d90eec4c6bf45699d28cf8d93e6..9b2d76722385ccf9d0ace747339ca7705ca41f4f 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,13 @@ 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/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -index 626cab5a974d2c8736123cc23e535b5cf0e5349e..feb1b516c7ac7200e7cebeea739369426e87bf27 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -@@ -53,6 +53,13 @@ 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/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index 2ac14783e7b5739a13c487d5028ecba38480980d..b5c4e127298795567ea4f35aa5a209ee6e1629b3 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -@@ -102,6 +102,13 @@ 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/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index 9b4b923117a7025bdbb6d222c6388aeae9bef8a2..5763d259162750297e08acc51551489150dbc593 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -127,6 +127,13 @@ 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 -@@ -627,7 +634,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 - Configurable entity base attributes - } - - @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 9fafe05ef0ffc1120873727082290a8ea177d62f..35d8cef3c84bfd1bbf8afe2885b4f303a4985cdd 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -102,6 +102,17 @@ 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/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index 69c291c3347a3e3f454ecb8f418a310bbd688a43..ca74450f13198fd7bf0190b4dc1df4288df5729d 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,13 @@ 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 - public void setPersistentAngerTarget(@Nullable UUID angryAt) { - this.persistentAngerTarget = angryAt; -@@ -262,7 +269,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 - Configurable entity base attributes - } - - @Nullable -diff --git a/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java b/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java -index 6716bfa903be5ab34b80c963cc9d6a8a26272621..1f37384368c26b4bdd69533887a8e9b8456f7096 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java -+++ b/src/main/java/net/minecraft/world/entity/monster/creaking/Creaking.java -@@ -129,6 +129,13 @@ 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/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index 4ab971e86b48ce3010928fe9046e8f68224719ca..476dcc613f566d88273f195b847e6b4dec777e44 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 -@@ -88,6 +88,13 @@ 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/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index b19e3b442a650f773df462e32088648a2b7eafd4..1a40babc8f6b3f56377cb2af45e9d17d0028a77b 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 -@@ -116,6 +116,13 @@ 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 - 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 3dc234f8cea8769f715a4913ae4ecf7d47433577..a0fa32a1217bbdae2c91e5a51598d7bd555ca1eb 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 -@@ -82,6 +82,13 @@ 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/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 066836e450ca8d104990c3c5fba5fe3dad2e136e..39f2091391222bcd4ab0f2677f896c5ecdc6e86c 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,13 @@ 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(); // 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 c5bef9f0ad2a0f57d8c37ea0833e899dc588d30f..8e12ae313c76d742b61aa83a35d41d83b92bded5 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -89,6 +89,12 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - } - // 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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 5004e86747306cc8d4bbed6f10d3a6e9047cb5f4..ee9bcb7d011f20575cbbbe2e0ded1e53087aba7a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -105,134 +105,261 @@ public class PurpurWorldConfig { - 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; - 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); - } - - public boolean axolotlRidable = false; - public boolean axolotlControllable = true; -+ public double axolotlMaxHealth = 14.0D; -+ public double axolotlScale = 1.0D; - 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); - } - - 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; - 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); - } - - 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; - 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); - } - - 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; - 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); - } - - 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; - 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; -+ public double catScale = 1.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); -+ catScale = Mth.clamp(getDouble("mobs.cat.attributes.scale", catScale), 0.0625D, 16.0D); - } - - public boolean caveSpiderRidable = false; - public boolean caveSpiderRidableInWater = true; - public boolean caveSpiderControllable = true; -+ public double caveSpiderMaxHealth = 12.0D; -+ public double caveSpiderScale = 1.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); -+ caveSpiderScale = Mth.clamp(getDouble("mobs.cave_spider.attributes.scale", caveSpiderScale), 0.0625D, 16.0D); - } - - public boolean chickenRidable = false; - public boolean chickenRidableInWater = false; - public boolean chickenControllable = true; -+ public double chickenMaxHealth = 4.0D; -+ public double chickenScale = 1.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); -+ chickenScale = Mth.clamp(getDouble("mobs.chicken.attributes.scale", chickenScale), 0.0625D, 16.0D); - } - - public boolean codRidable = false; - public boolean codControllable = true; -+ public double codMaxHealth = 3.0D; -+ public double codScale = 1.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); -+ codScale = Mth.clamp(getDouble("mobs.cod.attributes.scale", codScale), 0.0625D, 16.0D); - } - - public boolean cowRidable = false; - public boolean cowRidableInWater = true; - public boolean cowControllable = true; -+ public double cowMaxHealth = 10.0D; -+ public double cowScale = 1.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); -+ cowScale = Mth.clamp(getDouble("mobs.cow.attributes.scale", cowScale), 0.0625D, 16.0D); - } - - 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; - 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); - } - - public boolean dolphinRidable = false; -@@ -240,80 +367,175 @@ public class PurpurWorldConfig { - public int dolphinSpitCooldown = 20; - public float dolphinSpitSpeed = 1.0F; - public float dolphinSpitDamage = 2.0F; -+ public double dolphinMaxHealth = 10.0D; -+ public double dolphinScale = 1.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); -+ dolphinScale = Mth.clamp(getDouble("mobs.dolphin.attributes.scale", dolphinScale), 0.0625D, 16.0D); - } - - 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 drownedScale = 1.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); -+ drownedScale = Mth.clamp(getDouble("mobs.drowned.attributes.scale", drownedScale), 0.0625D, 16.0D); -+ drownedSpawnReinforcements = getDouble("mobs.drowned.attributes.spawn_reinforcements", drownedSpawnReinforcements); - } - - public boolean elderGuardianRidable = false; - public boolean elderGuardianControllable = true; -+ public double elderGuardianMaxHealth = 80.0D; -+ public double elderGuardianScale = 1.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); -+ elderGuardianScale = Mth.clamp(getDouble("mobs.elder_guardian.attributes.scale", elderGuardianScale), 0.0625D, 16.0D); - } - - 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; -+ public double endermanScale = 1.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); -+ endermanScale = Mth.clamp(getDouble("mobs.enderman.attributes.scale", endermanScale), 0.0625D, 16.0D); - } - - public boolean endermiteRidable = false; - public boolean endermiteRidableInWater = true; - public boolean endermiteControllable = true; -+ public double endermiteMaxHealth = 8.0D; -+ public double endermiteScale = 1.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); -+ endermiteScale = Mth.clamp(getDouble("mobs.endermite.attributes.scale", endermiteScale), 0.0625D, 16.0D); - } - - public boolean evokerRidable = false; - public boolean evokerRidableInWater = true; - public boolean evokerControllable = true; -+ public double evokerMaxHealth = 24.0D; -+ public double evokerScale = 1.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); -+ evokerScale = Mth.clamp(getDouble("mobs.evoker.attributes.scale", evokerScale), 0.0625D, 16.0D); - } - - public boolean foxRidable = false; - public boolean foxRidableInWater = true; - public boolean foxControllable = true; -+ public double foxMaxHealth = 10.0D; -+ public double foxScale = 1.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); -+ foxScale = Mth.clamp(getDouble("mobs.fox.attributes.scale", foxScale), 0.0625D, 16.0D); - } - - public boolean frogRidable = false; -@@ -331,147 +553,342 @@ public class PurpurWorldConfig { - public boolean ghastRidableInWater = true; - public boolean ghastControllable = true; - public double ghastMaxY = 320D; -+ public double ghastMaxHealth = 10.0D; -+ public double ghastScale = 1.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); -+ ghastScale = Mth.clamp(getDouble("mobs.ghast.attributes.scale", ghastScale), 0.0625D, 16.0D); - } - - 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; - 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); - } - - public boolean glowSquidRidable = false; - public boolean glowSquidControllable = true; -+ public double glowSquidMaxHealth = 10.0D; -+ public double glowSquidScale = 1.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); -+ glowSquidScale = Mth.clamp(getDouble("mobs.glow_squid.attributes.scale", glowSquidScale), 0.0625D, 16.0D); - } - - public boolean goatRidable = false; - public boolean goatRidableInWater = true; - public boolean goatControllable = true; -+ public double goatMaxHealth = 10.0D; -+ public double goatScale = 1.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); -+ goatScale = Mth.clamp(getDouble("mobs.goat.attributes.scale", goatScale), 0.0625D, 16.0D); - } - - public boolean guardianRidable = false; - public boolean guardianControllable = true; -+ public double guardianMaxHealth = 30.0D; -+ public double guardianScale = 1.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); -+ guardianScale = Mth.clamp(getDouble("mobs.guardian.attributes.scale", guardianScale), 0.0625D, 16.0D); - } - - public boolean hoglinRidable = false; - public boolean hoglinRidableInWater = true; - public boolean hoglinControllable = true; -+ public double hoglinMaxHealth = 40.0D; -+ public double hoglinScale = 1.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); -+ hoglinScale = Mth.clamp(getDouble("mobs.hoglin.attributes.scale", hoglinScale), 0.0625D, 16.0D); - } - - 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 huskScale = 1.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); -+ huskScale = Mth.clamp(getDouble("mobs.husk.attributes.scale", huskScale), 0.0625D, 16.0D); -+ 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; -+ public double illusionerScale = 1.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); -+ illusionerScale = Mth.clamp(getDouble("mobs.illusioner.attributes.scale", illusionerScale), 0.0625D, 16.0D); - } - - 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; - 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); - } - - 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; -+ public double mooshroomScale = 1.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); -+ mooshroomScale = Mth.clamp(getDouble("mobs.mooshroom.attributes.scale", mooshroomScale), 0.0625D, 16.0D); - } - - 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; -+ public double ocelotScale = 1.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); -+ ocelotScale = Mth.clamp(getDouble("mobs.ocelot.attributes.scale", ocelotScale), 0.0625D, 16.0D); - } - - public boolean pandaRidable = false; - public boolean pandaRidableInWater = true; - public boolean pandaControllable = true; -+ public double pandaMaxHealth = 20.0D; -+ public double pandaScale = 1.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); -+ pandaScale = Mth.clamp(getDouble("mobs.panda.attributes.scale", pandaScale), 0.0625D, 16.0D); - } - - 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; - 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); - } - - public boolean phantomRidable = false; -@@ -481,6 +898,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); -@@ -489,191 +910,405 @@ 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; -+ public double pigScale = 1.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); -+ pigScale = Mth.clamp(getDouble("mobs.pig.attributes.scale", pigScale), 0.0625D, 16.0D); - } - - public boolean piglinRidable = false; - public boolean piglinRidableInWater = true; - public boolean piglinControllable = true; -+ public double piglinMaxHealth = 16.0D; -+ public double piglinScale = 1.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); -+ piglinScale = Mth.clamp(getDouble("mobs.piglin.attributes.scale", piglinScale), 0.0625D, 16.0D); - } - - public boolean piglinBruteRidable = false; - public boolean piglinBruteRidableInWater = true; - public boolean piglinBruteControllable = true; -+ public double piglinBruteMaxHealth = 50.0D; -+ public double piglinBruteScale = 1.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); -+ piglinBruteScale = Mth.clamp(getDouble("mobs.piglin_brute.attributes.scale", piglinBruteScale), 0.0625D, 16.0D); - } - - public boolean pillagerRidable = false; - public boolean pillagerRidableInWater = true; - public boolean pillagerControllable = true; -+ public double pillagerMaxHealth = 24.0D; -+ public double pillagerScale = 1.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); -+ pillagerScale = Mth.clamp(getDouble("mobs.pillager.attributes.scale", pillagerScale), 0.0625D, 16.0D); - } - - public boolean polarBearRidable = false; - public boolean polarBearRidableInWater = true; - public boolean polarBearControllable = true; -+ public double polarBearMaxHealth = 30.0D; -+ public double polarBearScale = 1.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); -+ polarBearScale = Mth.clamp(getDouble("mobs.polar_bear.attributes.scale", polarBearScale), 0.0625D, 16.0D); - } - - public boolean pufferfishRidable = false; - public boolean pufferfishControllable = true; -+ public double pufferfishMaxHealth = 3.0D; -+ public double pufferfishScale = 1.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); -+ pufferfishScale = Mth.clamp(getDouble("mobs.pufferfish.attributes.scale", pufferfishScale), 0.0625D, 16.0D); - } - - public boolean rabbitRidable = false; - public boolean rabbitRidableInWater = true; - public boolean rabbitControllable = true; -+ public double rabbitMaxHealth = 3.0D; -+ public double rabbitScale = 1.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); -+ rabbitScale = Mth.clamp(getDouble("mobs.rabbit.attributes.scale", rabbitScale), 0.0625D, 16.0D); - } - - public boolean ravagerRidable = false; - public boolean ravagerRidableInWater = false; - public boolean ravagerControllable = true; -+ public double ravagerMaxHealth = 100.0D; -+ public double ravagerScale = 1.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); -+ ravagerScale = Mth.clamp(getDouble("mobs.ravager.attributes.scale", ravagerScale), 0.0625D, 16.0D); - } - - public boolean salmonRidable = false; - public boolean salmonControllable = true; -+ public double salmonMaxHealth = 3.0D; -+ public double salmonScale = 1.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); -+ salmonScale = Mth.clamp(getDouble("mobs.salmon.attributes.scale", salmonScale), 0.0625D, 16.0D); - } - - public boolean sheepRidable = false; - public boolean sheepRidableInWater = true; - public boolean sheepControllable = true; -+ public double sheepMaxHealth = 8.0D; -+ public double sheepScale = 1.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); -+ sheepScale = Mth.clamp(getDouble("mobs.sheep.attributes.scale", sheepScale), 0.0625D, 16.0D); - } - - public boolean shulkerRidable = false; - public boolean shulkerRidableInWater = true; - public boolean shulkerControllable = true; -+ public double shulkerMaxHealth = 30.0D; -+ public double shulkerScale = 1.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); -+ shulkerScale = Mth.clamp(getDouble("mobs.shulker.attributes.scale", shulkerScale), 0.0625D, Shulker.MAX_SCALE); - } - - 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; - 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); - } - - public boolean skeletonRidable = false; - public boolean skeletonRidableInWater = true; - public boolean skeletonControllable = true; -+ public double skeletonMaxHealth = 20.0D; -+ public double skeletonScale = 1.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); -+ skeletonScale = Mth.clamp(getDouble("mobs.skeleton.attributes.scale", skeletonScale), 0.0625D, 16.0D); - } - - 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; - 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); - } - - 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; -+ public double snowGolemScale = 1.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); -+ snowGolemScale = Mth.clamp(getDouble("mobs.snow_golem.attributes.scale", snowGolemScale), 0.0625D, 16.0D); - } - - public boolean snifferRidable = false; - public boolean snifferRidableInWater = true; - public boolean snifferControllable = true; -+ public double snifferMaxHealth = 14.0D; -+ public double snifferScale = 1.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); -+ snifferScale = Mth.clamp(getDouble("mobs.sniffer.attributes.scale", snifferScale), 0.0625D, 16.0D); - } - - public boolean squidRidable = false; - public boolean squidControllable = true; -+ public double squidMaxHealth = 10.0D; -+ public double squidScale = 1.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); -+ squidScale = Mth.clamp(getDouble("mobs.squid.attributes.scale", squidScale), 0.0625D, 16.0D); - } - - public boolean spiderRidable = false; - public boolean spiderRidableInWater = false; - public boolean spiderControllable = true; -+ public double spiderMaxHealth = 16.0D; -+ public double spiderScale = 1.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); -+ spiderScale = Mth.clamp(getDouble("mobs.spider.attributes.scale", spiderScale), 0.0625D, 16.0D); - } - - public boolean strayRidable = false; - public boolean strayRidableInWater = true; - public boolean strayControllable = true; -+ public double strayMaxHealth = 20.0D; -+ public double strayScale = 1.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); -+ strayScale = Mth.clamp(getDouble("mobs.stray.attributes.scale", strayScale), 0.0625D, 16.0D); - } - - public boolean striderRidable = false; - public boolean striderRidableInWater = false; - public boolean striderControllable = true; -+ public double striderMaxHealth = 20.0D; -+ public double striderScale = 1.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); -+ striderScale = Mth.clamp(getDouble("mobs.strider.attributes.scale", striderScale), 0.0625D, 16.0D); - } - - public boolean tadpoleRidable = false; -@@ -688,64 +1323,137 @@ 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; -+ public double tropicalFishScale = 1.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); -+ tropicalFishScale = Mth.clamp(getDouble("mobs.tropical_fish.attributes.scale", tropicalFishScale), 0.0625D, 16.0D); - } - - public boolean turtleRidable = false; - public boolean turtleRidableInWater = true; - public boolean turtleControllable = true; -+ public double turtleMaxHealth = 30.0D; -+ public double turtleScale = 1.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); -+ turtleScale = Mth.clamp(getDouble("mobs.turtle.attributes.scale", turtleScale), 0.0625D, 16.0D); - } - - 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; - 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); - } - - public boolean villagerRidable = false; - public boolean villagerRidableInWater = true; - public boolean villagerControllable = true; -+ public double villagerMaxHealth = 20.0D; -+ public double villagerScale = 1.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); -+ villagerScale = Mth.clamp(getDouble("mobs.villager.attributes.scale", villagerScale), 0.0625D, 16.0D); - } - - public boolean vindicatorRidable = false; - public boolean vindicatorRidableInWater = true; - public boolean vindicatorControllable = true; -+ public double vindicatorMaxHealth = 24.0D; -+ public double vindicatorScale = 1.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); -+ vindicatorScale = Mth.clamp(getDouble("mobs.vindicator.attributes.scale", vindicatorScale), 0.0625D, 16.0D); - } - - public boolean wanderingTraderRidable = false; - public boolean wanderingTraderRidableInWater = true; - public boolean wanderingTraderControllable = true; -+ public double wanderingTraderMaxHealth = 20.0D; -+ public double wanderingTraderScale = 1.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); -+ wanderingTraderScale = Mth.clamp(getDouble("mobs.wandering_trader.attributes.scale", wanderingTraderScale), 0.0625D, 16.0D); - } - - public boolean wardenRidable = false; -@@ -760,83 +1468,183 @@ public class PurpurWorldConfig { - public boolean witchRidable = false; - public boolean witchRidableInWater = true; - public boolean witchControllable = true; -+ public double witchMaxHealth = 26.0D; -+ public double witchScale = 1.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); -+ witchScale = Mth.clamp(getDouble("mobs.witch.attributes.scale", witchScale), 0.0625D, 16.0D); - } - - 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; - 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); - } - - public boolean witherSkeletonRidable = false; - public boolean witherSkeletonRidableInWater = true; - public boolean witherSkeletonControllable = true; -+ public double witherSkeletonMaxHealth = 20.0D; -+ public double witherSkeletonScale = 1.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); -+ witherSkeletonScale = Mth.clamp(getDouble("mobs.wither_skeleton.attributes.scale", witherSkeletonScale), 0.0625D, 16.0D); - } - - public boolean wolfRidable = false; - public boolean wolfRidableInWater = true; - public boolean wolfControllable = true; -+ public double wolfMaxHealth = 8.0D; -+ public double wolfScale = 1.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); -+ wolfScale = Mth.clamp(getDouble("mobs.wolf.attributes.scale", wolfScale), 0.0625D, 16.0D); - } - - public boolean zoglinRidable = false; - public boolean zoglinRidableInWater = true; - public boolean zoglinControllable = true; -+ public double zoglinMaxHealth = 40.0D; -+ public double zoglinScale = 1.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); -+ zoglinScale = Mth.clamp(getDouble("mobs.zoglin.attributes.scale", zoglinScale), 0.0625D, 16.0D); - } - - 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; - 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); - } - - 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; - 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); - } - - 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; - 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); - } - - 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; - 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); - } - } diff --git a/patches/server/0009-Barrels-and-enderchests-6-rows.patch b/patches/server/0009-Barrels-and-enderchests-6-rows.patch deleted file mode 100644 index 49b38c91d7..0000000000 --- a/patches/server/0009-Barrels-and-enderchests-6-rows.patch +++ /dev/null @@ -1,298 +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 98e803eaf5ce4c773f35fd752c21c7176707427c..b9136fcec3f36549ddd572cbb8cd01ecae45590f 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -1116,6 +1116,27 @@ public abstract class PlayerList { - player.getBukkitEntity().recalculatePermissions(); // CraftBukkit - this.server.getCommands().sendCommands(player); - } // Paper - Add sendOpLevel API -+ -+ // 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 28a4cf814ec4b34dce883ba4f24ca55008c8f6c1..87c6378104ff47549c751e09afb6cfcd9b8dcf5d 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -200,6 +200,7 @@ public abstract class Player extends LivingEntity { - private int currentImpulseContextResetGraceTime; - 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 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 48a6b6136ac3414ca735f93a14b1a8d76210603c..27321b07cd04814bc1ff720c65770d7755625bb6 100644 ---- a/src/main/java/net/minecraft/world/inventory/ChestMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/ChestMenu.java -@@ -66,10 +66,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 a15d5ff872dbd77f3c3145e0328f3d02e431ff8c..1dcf36d502990d32fc4cd3ea69c3ea334baed69a 100644 ---- a/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java -+++ b/src/main/java/net/minecraft/world/inventory/PlayerEnderChestContainer.java -@@ -31,11 +31,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 9b6ab617ab7f503cf0b2d4e29333c706ffe95f46..bfe79431dc5707677671df5c0787817c6e14a676 100644 ---- a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java -@@ -84,7 +84,7 @@ public class EnderChestBlock extends AbstractChestBlock i - // Paper start - Fix InventoryOpenEvent cancellation - moved up; - playerEnderChestContainer.setActiveChest(enderChestBlockEntity); // Needs to happen before ChestMenu.threeRows as it is required for opening animations - if (world instanceof ServerLevel serverLevel && player.openMenu( -- new SimpleMenuProvider((i, inventory, playerx) -> ChestMenu.threeRows(i, inventory, playerEnderChestContainer), CONTAINER_TITLE) -+ new SimpleMenuProvider((i, inventory, playerx) -> org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? getEnderChestSixRows(i, inventory, player, playerEnderChestContainer) : ChestMenu.threeRows(i, inventory, playerEnderChestContainer), CONTAINER_TITLE) // Purpur - ).isPresent()) { - // Paper end - Fix InventoryOpenEvent cancellation - moved up; - // Paper - Fix InventoryOpenEvent cancellation - moved up; -@@ -99,6 +99,35 @@ public class EnderChestBlock extends AbstractChestBlock i - } - } - -+ // Purpur start -+ 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 -+ - @Override - public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { - return new EnderChestBlockEntity(pos, state); -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 618552afbdacc919c33b30a6bf4834fb71ab3d5b..7a059d20abdcc0073a314311d78f63ae4bd0365e 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 -@@ -68,7 +68,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) { -@@ -119,7 +128,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 -@@ -139,7 +157,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 6d3f9d5dab6c9a2860ae31cae24310aa2d62da7c..4f29c579f94efe59a8c78520d75676fc4875e2f0 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java -@@ -145,8 +145,19 @@ public class CraftContainer extends AbstractContainerMenu { - case PLAYER: - case CHEST: - case ENDER_CHEST: -+ // Purpur start -+ 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 - 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 c6159c70f7a37b9bffe268b91905ce848d1d2927..d02adaaa6fbdc1c0eff44cb4a1f1642f9575a821 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java -@@ -84,7 +84,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 e8c9393760108f2549b52a61d973ea2b4a64a312..1321955eb23272d96e3028c1c46e75f6c1eaade0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -180,4 +180,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/0010-Llama-API.patch b/patches/server/0010-Llama-API.patch deleted file mode 100644 index 9f35a00aca..0000000000 --- a/patches/server/0010-Llama-API.patch +++ /dev/null @@ -1,91 +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 df695b444fa2a993d381e2f197182c3e91a68502..eb0faf58fa1a408f294fc62120b140def97f998d 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.0, 4.0, 9.0), 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.0) { -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 83fdbf55384a5c4429d65a88fcb788e449a8862a..cbab482bd44c0a0fa82a80f41fdfd8c124c58c43 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 -@@ -72,6 +72,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder type, Level world) { - super(type, world); -@@ -167,6 +168,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder -Date: Thu, 8 Aug 2019 15:29:15 -0500 -Subject: [PATCH] AFK API - - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 8207208d6fb3f982e9909add9e74a0dda69e8120..5e8a5e8e4120420a170c4733ae217e7948cef46c 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -2621,8 +2621,68 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - - public void resetLastActionTime() { - this.lastActionTime = Util.getMillis(); -+ this.setAfk(false); // Purpur - } - -+ // 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, !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, true); -+ } -+ } -+ -+ ((ServerLevel) this.level()).updateSleepingPlayerList(); -+ } -+ -+ @Override -+ public boolean isAfk() { -+ return this.isAfk; -+ } -+ -+ @Override -+ public boolean canBeCollidedWith() { -+ return !this.isAfk() && super.canBeCollidedWith(); -+ } -+ // Purpur end - AFK API -+ - 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 fbc59503316d566e88b037851afd74e5469c281b..830e0705f3a3d45fcf0eab6b8eea403c3023f3f9 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -344,6 +344,20 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - private boolean justTeleported = false; - // CraftBukkit end - -+ // 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(CraftPlayer player) { -+ return player.hasPermission("purpur.bypassIdleKick"); -+ } -+ } -+ ); -+ // Purpur end - AFK API -+ - @Override - public void tick() { - if (this.ackBlockChangesUpTo > -1) { -@@ -400,6 +414,12 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - this.recipeSpamPackets.tick(); // Paper - auto recipe limit - this.dropSpamThrottler.tick(); - 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 - 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.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854 - this.disconnect((Component) Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause - } -@@ -656,6 +676,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - 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); -@@ -1554,7 +1576,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - movedWrongly = true; - if (event.getLogWarning()) - // Paper end -- ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!", this.player.getName().getString()); -+ ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!, ({})", this.player.getName().getString(), d11); // Purpur - AFK API - } // Paper - } - -@@ -1622,6 +1644,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - 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); -diff --git a/src/main/java/net/minecraft/server/players/SleepStatus.java b/src/main/java/net/minecraft/server/players/SleepStatus.java -index 823efad652d8ff9e96b99375b102fef6f017716e..bdf0240840d92bf95f94c6fb1125eeaa105e303b 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 - AFK API - 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 - AFK API - ++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 6bf691fcc6486bde73bae30eff09142802c29eda..d99b223be90f0c04bb9274228ad323a7c7f218b2 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 - Ability to control player's insomnia and phantoms -+ public static Predicate notAfk = (player) -> !player.isAfk(); // Purpur - AFK API - - private EntitySelector() {} - // Paper start - Affects Spawning API -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 52982c1e6a4da36392569c791853279f5f9ac31a..ebb827e213a3ba5eeb2fe5b78f5dee99403097b6 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(target, world)) { - return false; -+ // Purpur start - AFK API -+ } else if (!world.purpurConfig.idleTimeoutTargetPlayer && target instanceof net.minecraft.server.level.ServerPlayer player && player.isAfk()) { -+ return false; -+ // Purpur end - AFK API - } else { - if (tester == null) { - if (this.isCombat && (!target.canBeSeenAsEnemy() || world.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 87c6378104ff47549c751e09afb6cfcd9b8dcf5d..2f69a511db8d43fbd3a17387cded1d3573579fce 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -206,6 +206,13 @@ public abstract class Player extends LivingEntity { - 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 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 5d7a6e4b73f032db356e7ec369b150013e940ee6..e164833de0c29eed9025dd4af3f2bb74c92d2250 100644 ---- a/src/main/java/net/minecraft/world/level/EntityGetter.java -+++ b/src/main/java/net/minecraft/world/level/EntityGetter.java -@@ -184,7 +184,7 @@ public interface EntityGetter extends ca.spottedleaf.moonrise.patches.chunk_syst - - 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)) { // Purpur - AFK API - double d = player.distanceToSqr(x, y, z); - if (range < 0.0 || 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 c5bd2a45b32e8dff83c148379544db125684622a..16671636da1b6da62a0bdf0daab745fcd89143b7 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -584,10 +584,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - - @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 : (List) this.server.getHandle().players) { - if (player.getBukkitEntity().canSee(this)) { -@@ -3572,4 +3577,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - return getHandle().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 - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 1321955eb23272d96e3028c1c46e75f6c1eaade0..82f0ae89ef3f0dc614edb4ccd3caa66cb387044c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -177,8 +177,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 ee9bcb7d011f20575cbbbe2e0ded1e53087aba7a..9b1a4502aa6c26c7524ec17697250317b7f381fd 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -91,6 +91,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 1d438ef44cbe4d1eedfba36d8fe5d2ad53464921..24d7eca3f0b06602a1026eda3432f0a4255d8b01 100644 ---- a/src/main/java/org/spigotmc/ActivationRange.java -+++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -199,6 +199,8 @@ 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/0012-Bring-back-server-name.patch b/patches/server/0012-Bring-back-server-name.patch deleted file mode 100644 index 6ec709f89b..0000000000 --- a/patches/server/0012-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 c3ec370b83b895be0f03662e3884fa4a2442a2a6..05e16103af3fd276f0196ddf1a2e5b729b025c34 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -@@ -56,6 +56,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 ce4ce361061932162ace58070d44d1aa70189dbd..d61f6cf5a5d3320516339a7078f2e9fb2ab59dfa 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -2013,7 +2013,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop -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 d61f6cf5a5d3320516339a7078f2e9fb2ab59dfa..18dc66b78f91c0b9efdf9008c0a38c52fdfc93ad 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -329,6 +329,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping -+ public boolean lagging = false; // Purpur - Lagging threshold - - public volatile Thread shutdownThread; // Paper - public volatile boolean abnormalExit = false; // Paper -@@ -1304,6 +1305,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 d23914a3ab3723d532ae867db6b954c843030f75..426d47c14d137906ed70fc3082e3d77e7f48c8d7 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityType.java -+++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -404,6 +404,16 @@ public class EntityType implements FeatureElement, EntityTypeT - return EntityType.register(EntityType.vanillaEntityId(id), type); - } - -+ // Purpur start -+ public static EntityType getFromBukkitType(org.bukkit.entity.EntityType bukkitType) { -+ return getFromKey(ResourceLocation.parse(bukkitType.getKey().toString())); -+ } -+ -+ public static EntityType getFromKey(ResourceLocation location) { -+ return BuiltInRegistries.ENTITY_TYPE.getValue(location); -+ } -+ // Purpur end -+ - public static ResourceLocation getKey(EntityType type) { - return BuiltInRegistries.ENTITY_TYPE.getKey(type); - } -@@ -614,6 +624,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() { - return this.descriptionId; - } -diff --git a/src/main/java/net/minecraft/world/item/SpawnEggItem.java b/src/main/java/net/minecraft/world/item/SpawnEggItem.java -index cc7e9b87e919b4ef8cf77cd780c890fd9a9cfa50..a185d098175e504b7bb93d2cff03ca99eabc11eb 100644 ---- a/src/main/java/net/minecraft/world/item/SpawnEggItem.java -+++ b/src/main/java/net/minecraft/world/item/SpawnEggItem.java -@@ -68,6 +68,23 @@ public class SpawnEggItem extends Item { - Spawner spawner = (Spawner) tileentity; - - entitytypes = this.getType(world.registryAccess(), itemstack); -+ // Purpur start -+ if (spawner instanceof net.minecraft.world.level.block.entity.SpawnerBlockEntity) { -+ 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()); -+ } else if (spawner instanceof net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity) { -+ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.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(entitytypes.getName())); -+ if (!event.callEvent()) { -+ return InteractionResult.FAIL; -+ } -+ entitytypes = EntityType.getFromBukkitType(event.getEntityType()); -+ } -+ // Purpur end - spawner.setEntityId(entitytypes, world.getRandom()); - world.sendBlockUpdated(blockposition, iblockdata, iblockdata, 3); - world.gameEvent((Entity) context.getPlayer(), (Holder) GameEvent.BLOCK_CHANGE, blockposition); diff --git a/patches/server/0016-Anvil-API.patch b/patches/server/0016-Anvil-API.patch deleted file mode 100644 index 7cea852864..0000000000 --- a/patches/server/0016-Anvil-API.patch +++ /dev/null @@ -1,225 +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 4680f77a275d8d2b226018db89a571ac25998dd8..bfc90524bd739ed1d91fe9912e38093b3c28928f 100644 ---- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java -@@ -80,6 +80,7 @@ public abstract class AbstractContainerMenu { - @Nullable - private ContainerSynchronizer synchronizer; - private boolean suppressRemoteUpdates; -+ @Nullable protected ItemStack activeQuickItem = null; // Purpur - Anvil API - - // 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 286ae002e1711ad9e800b7f2091988d66cd572a7..f8c0a4fd95f341cbf8f6a06dfae408d505b0f018 100644 ---- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -@@ -25,6 +25,12 @@ import org.slf4j.Logger; - import org.bukkit.craftbukkit.inventory.view.CraftAnvilView; - // CraftBukkit end - -+// 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; -@@ -55,6 +61,10 @@ public class AnvilMenu extends ItemCombinerMenu { - private 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 syncId, Inventory inventory) { - this(syncId, inventory, ContainerLevelAccess.NULL); -@@ -82,12 +92,17 @@ public class AnvilMenu extends ItemCombinerMenu { - - @Override - protected boolean mayPickup(Player player, boolean present) { -- return (player.hasInfiniteMaterials() || player.experienceLevel >= this.cost.get()) && this.cost.get() > AnvilMenu.DEFAULT_DENIED_COST && present; // 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) && present; // 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.getAbilities().instabuild) { -+ if (this.bypassCost) ((ServerPlayer) player).lastSentExp = -1; else // Purpur - Anvil API - player.giveExperienceLevels(-this.cost.get()); - } - -@@ -138,6 +153,12 @@ public class AnvilMenu extends ItemCombinerMenu { - - @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 itemstack = this.inputSlots.getItem(0); - - this.onlyRenaming = false; -@@ -146,7 +167,7 @@ public class AnvilMenu extends ItemCombinerMenu { - long j = 0L; - byte b0 = 0; - -- if (!itemstack.isEmpty() && EnchantmentHelper.canStoreEnchantments(itemstack)) { -+ if (!itemstack.isEmpty() && this.canDoUnsafeEnchants || EnchantmentHelper.canStoreEnchantments(itemstack)) { // Purpur - Anvil API - ItemStack itemstack1 = itemstack.copy(); - ItemStack itemstack2 = this.inputSlots.getItem(1); - ItemEnchantments.Mutable itemenchantments_a = new ItemEnchantments.Mutable(EnchantmentHelper.getEnchantmentsForCrafting(itemstack1)); -@@ -225,7 +246,7 @@ public class AnvilMenu extends ItemCombinerMenu { - Holder holder1 = (Holder) iterator1.next(); - - if (!holder1.equals(holder) && !Enchantment.areCompatible(holder, holder1)) { -- flag3 = false; -+ flag3 = this.canDoUnsafeEnchants; // Purpur - Anvil API - ++i; - } - } -@@ -287,6 +308,12 @@ public class AnvilMenu extends ItemCombinerMenu { - 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.getAbilities().instabuild) { // CraftBukkit - itemstack1 = ItemStack.EMPTY; - } -@@ -307,6 +334,13 @@ public class AnvilMenu extends ItemCombinerMenu { - - org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(this.getBukkitView(), itemstack1); // CraftBukkit - this.broadcastChanges(); -+ -+ // Purpur start - Anvil API -+ if (this.canDoUnsafeEnchants && itemstack1 != ItemStack.EMPTY) { -+ ((ServerPlayer) this.player).connection.send(new ClientboundContainerSetSlotPacket(this.containerId, this.incrementStateId(), 2, itemstack1)); -+ ((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 -diff --git a/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java b/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java -index a5d53a656513ae81cc3f9fc506caf6adaba62a8e..ac9df238ef0f3d009f25976b95e0b750e963e952 100644 ---- a/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java -@@ -164,7 +164,9 @@ public abstract class ItemCombinerMenu extends AbstractContainerMenu { - return ItemStack.EMPTY; - } - -+ this.activeQuickItem = itemstack; // Purpur - Anvil API - slot1.onTake(player, itemstack1); -+ this.activeQuickItem = null; // Purpur - Anvil API - } - - 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 792cb6adf0c7a6335cc5985fce8bed2e0f1149af..5734c5caffda79383ae30df20c3defb51b87f39e 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java -@@ -19,6 +19,10 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn - 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 +31,10 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn - 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 +121,30 @@ public class CraftInventoryAnvil extends CraftResultInventory implements AnvilIn - 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/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java -index f86c95a13dff012de5db3e41ac261e9e8d44d9f3..1db0b790d824e419bb5fb6ab1f3003e120f9763b 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java -@@ -75,4 +75,26 @@ public class CraftAnvilView extends CraftInventoryView -Date: Fri, 11 Oct 2019 00:17:39 -0500 -Subject: [PATCH] Alternative Keepalive Handling - - -diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -index 92749b57d3a2b2ffee79436319513248846296b6..df48c2754dc1ebd52addd8ae768cba5916ce3969 100644 ---- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -@@ -80,6 +80,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - private long keepAliveChallenge; - private long closedListenerTime; - private boolean closed = false; -+ private it.unimi.dsi.fastutil.longs.LongList keepAlives = new it.unimi.dsi.fastutil.longs.LongArrayList(); // Purpur - private int latency; - private volatile boolean suspendFlushingOnServerThread = false; - public final java.util.Map packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks -@@ -137,6 +138,16 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - - @Override - public void handleKeepAlive(ServerboundKeepAlivePacket packet) { -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.useAlternateKeepAlive) { -+ if (this.keepAlivePending && !keepAlives.isEmpty() && keepAlives.contains(packet.getId())) { -+ int ping = (int) (Util.getMillis() - packet.getId()); -+ this.latency = (this.latency * 3 + ping) / 4; -+ this.keepAlivePending = false; -+ 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 - handle ServerboundKeepAlivePacket async - if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) { - int i = (int) (Util.getMillis() - this.keepAliveTime); -@@ -269,6 +280,21 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - long currentTime = Util.getMillis(); - long elapsedTime = currentTime - this.keepAliveTime; - -+ // Purpur start -+ if (org.purpurmc.purpur.PurpurConfig.useAlternateKeepAlive) { -+ if (elapsedTime >= 1000L) { // 1 second -+ if (this.keepAlivePending && !this.processedDisconnect && keepAlives.size() * 1000L >= KEEPALIVE_LIMIT) { -+ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); -+ } else if (this.checkIfClosed(currentTime)) { -+ this.keepAlivePending = true; -+ this.keepAliveTime = currentTime; // hijack this field for 1 second intervals -+ this.keepAlives.add(currentTime); // currentTime is ID -+ this.send(new ClientboundKeepAlivePacket(currentTime)); -+ } -+ } -+ } else -+ // Purpur end -+ - if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // Paper - use vanilla's 15000L between keep alive packets - if (this.keepAlivePending && !this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // Paper - check keepalive limit, don't fire if already disconnected - this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index bfa985913f3f750a0727a567d3b5951b4d6ed4ca..2ca6cc6ba72339459d9591a89995521a3c81d111 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -201,6 +201,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/0018-Enchantment-convenience-methods.patch b/patches/server/0018-Enchantment-convenience-methods.patch deleted file mode 100644 index 573bafb9b4..0000000000 --- a/patches/server/0018-Enchantment-convenience-methods.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Krakenied -Date: Sat, 15 Jun 2024 14:31:48 +0200 -Subject: [PATCH] Enchantment convenience methods - - -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 4dd074d04c9a535f6cf24420058fd68594c59edc..64c315372277300e58ce413210f47eed5b6e1d6c 100644 ---- a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java -+++ b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java -@@ -578,4 +578,14 @@ public class EnchantmentHelper { - 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 - } diff --git a/patches/server/0019-Silk-touch-spawners.patch b/patches/server/0019-Silk-touch-spawners.patch deleted file mode 100644 index d867ba5964..0000000000 --- a/patches/server/0019-Silk-touch-spawners.patch +++ /dev/null @@ -1,190 +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/BlockItem.java b/src/main/java/net/minecraft/world/item/BlockItem.java -index 4377fa2400c4320e0023ece230090a2a3b4b2ab6..ac5dc472337cd9613db5fc03f64763245129dfd9 100644 ---- a/src/main/java/net/minecraft/world/item/BlockItem.java -+++ b/src/main/java/net/minecraft/world/item/BlockItem.java -@@ -223,6 +223,7 @@ public class BlockItem extends Item { - } - - if (tileentitytypes1.onlyOpCanSetNbt() && (player == null || !(player.canUseGameMasterBlocks() || (player.getAbilities().instabuild && player.getBukkitEntity().hasPermission("minecraft.nbt.place"))))) { // Spigot - add permission -+ if (!(world.purpurConfig.silkTouchEnabled && tileentity instanceof net.minecraft.world.level.block.entity.SpawnerBlockEntity && player.getBukkitEntity().hasPermission("purpur.drop.spawners"))) - return false; - } - -diff --git a/src/main/java/net/minecraft/world/item/Items.java b/src/main/java/net/minecraft/world/item/Items.java -index 6d16b4433e79eca0ff8008941f0b9b807b1db9db..41fbe61268c0a16078b5f846ab12bde172872ff7 100644 ---- a/src/main/java/net/minecraft/world/item/Items.java -+++ b/src/main/java/net/minecraft/world/item/Items.java -@@ -367,7 +367,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(Blocks.SPAWNER, org.purpurmc.purpur.item.SpawnerItem::new, new Item.Properties().rarity(Rarity.EPIC)); // Purpur - public static final Item CREAKING_HEART = registerBlock(Blocks.CREAKING_HEART); - public static final Item CHEST = registerBlock(Blocks.CHEST, settings -> settings.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY)); - public static final Item CRAFTING_TABLE = registerBlock(Blocks.CRAFTING_TABLE); -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 d751b280a8bf2066d458f8eb548d7aa123fa69c9..fb88b5c1d9e764bf1211d601527133ea8e8268b3 100644 ---- a/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SpawnerBlock.java -@@ -42,6 +42,57 @@ 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, @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) { -+ type = net.minecraft.world.entity.EntityType.by(nextSpawnData.getEntityToSpawn()); -+ net.minecraft.world.level.SpawnData.CODEC.encodeStart(net.minecraft.nbt.NbtOps.INSTANCE, nextSpawnData).result().ifPresent(tag -> item.set(net.minecraft.core.component.DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY.update(compoundTag -> compoundTag.put("Purpur.SpawnData", tag)))); -+ } -+ -+ 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)); -+ } -+ -+ List lore = level.purpurConfig.silkTouchSpawnerLore; -+ if (lore != null && !lore.isEmpty()) { -+ -+ 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.HIDE_ADDITIONAL_TOOLTIP, net.minecraft.util.Unit.INSTANCE); -+ } -+ 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 -+ - @Override - protected void spawnAfterBreak(BlockState state, ServerLevel world, BlockPos pos, ItemStack tool, boolean dropExperience) { - super.spawnAfterBreak(state, world, pos, tool, dropExperience); -@@ -50,6 +101,7 @@ public class SpawnerBlock extends BaseEntityBlock { - - @Override - public int getExpDrop(BlockState iblockdata, ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, boolean flag) { -+ if (worldserver.purpurConfig.silkTouchEnabled && 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 9b1a4502aa6c26c7524ec17697250317b7f381fd..d6b1b49102ae4421e273f6c781e25a3b18fe13f7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -109,6 +109,38 @@ public class PurpurWorldConfig { - idleTimeoutTargetPlayer = getBoolean("gameplay-mechanics.player.idle-timeout.mobs-target", idleTimeoutTargetPlayer); - } - -+ 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(ResourceLocation.parse(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..ed50cb2115401c9039df4136caf5a087a5f5991c ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java -@@ -0,0 +1,40 @@ -+package org.purpurmc.purpur.item; -+ -+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(); -+ if (customData.contains("Purpur.mob_type")) { -+ EntityType.byString(customData.getString("Purpur.mob_type")).ifPresent(type -> spawner.getSpawner().setEntityId(type, level, level.random, pos)); -+ } else if (customData.contains("Purpur.SpawnData")) { -+ net.minecraft.world.level.SpawnData.CODEC.parse(net.minecraft.nbt.NbtOps.INSTANCE, customData.getCompound("Purpur.SpawnData")).result() -+ .ifPresent(spawnData -> spawner.getSpawner().nextSpawnData = spawnData); -+ } -+ } -+ } -+ return handled; -+ } -+} diff --git a/patches/server/0020-Add-turtle-egg-block-options.patch b/patches/server/0020-Add-turtle-egg-block-options.patch deleted file mode 100644 index adc80aaa76..0000000000 --- a/patches/server/0020-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 953ddb2ea6fd48e57712e30a6addf23e188e5312..df2028f53fd07551f8aa7eeb49f99ac0676d5fe0 100644 ---- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -@@ -204,6 +204,25 @@ public class TurtleEggBlock extends Block { - } - - private boolean canDestroyEgg(ServerLevel 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 - Add turtle egg block options -+ 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 - Add turtle egg block options - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d6b1b49102ae4421e273f6c781e25a3b18fe13f7..f6186c65468953756da9c381c6ee712115a244f7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -141,6 +141,15 @@ public class PurpurWorldConfig { - }); - } - -+ public boolean turtleEggsBreakFromExpOrbs = false; -+ public boolean turtleEggsBreakFromItems = false; -+ public boolean turtleEggsBreakFromMinecarts = 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); -+ } -+ - public boolean babiesAreRidable = true; - public boolean untamedTamablesAreRidable = true; - public boolean useNightVisionWhenRiding = false; diff --git a/patches/server/0021-Logger-settings-suppressing-pointless-logs.patch b/patches/server/0021-Logger-settings-suppressing-pointless-logs.patch deleted file mode 100644 index a15b3bc8be..0000000000 --- a/patches/server/0021-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 8e2eb7b61421ceb063654826941f1a81f6f50bdf..0a16aa193ef24aa8f1716f9e089b8027fa3c0a3c 100644 ---- a/src/main/java/net/minecraft/server/PlayerAdvancements.java -+++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java -@@ -199,6 +199,7 @@ public class PlayerAdvancements { - - if (advancementholder == null) { - if (!minecraftkey.getNamespace().equals("minecraft")) return; // CraftBukkit -+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressIgnoredAdvancementWarnings) // Purpur - PlayerAdvancements.LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", minecraftkey, this.playerSavePath); - } else { - this.startProgress(advancementholder, advancementprogress); -diff --git a/src/main/java/net/minecraft/server/level/WorldGenRegion.java b/src/main/java/net/minecraft/server/level/WorldGenRegion.java -index e4b0dc3121101d54394a0c3a413dabf8103b2ea6..a8484b9659f175cc20985bf66082616ceb31df4d 100644 ---- a/src/main/java/net/minecraft/server/level/WorldGenRegion.java -+++ b/src/main/java/net/minecraft/server/level/WorldGenRegion.java -@@ -336,6 +336,7 @@ public class WorldGenRegion implements WorldGenLevel { - return true; - } else { - // Paper start - Buffer OOB setBlock calls -+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressSetBlockFarChunk) // Purpur - if (!hasSetFarWarned) { - Util.logAndPauseIfInIde("Detected setBlock in a far chunk [" + i + ", " + j + "], pos: " + String.valueOf(pos) + ", status: " + String.valueOf(this.generatingStep.targetStatus()) + (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 5c7484ce2850a2eb698a2183b81134b89b0bbcc7..fef09e71f73f6f24a01289ef05c20a5ffc5fadb1 100644 ---- a/src/main/java/net/minecraft/stats/ServerRecipeBook.java -+++ b/src/main/java/net/minecraft/stats/ServerRecipeBook.java -@@ -159,6 +159,7 @@ public class ServerRecipeBook extends RecipeBook { - ResourceKey> resourcekey = ResourceKey.create(Registries.RECIPE, ResourceLocation.parse(s)); - - if (!validPredicate.test(resourcekey)) { -+ if (!org.purpurmc.purpur.PurpurConfig.loggerSuppressUnrecognizedRecipeErrors) // Purpur - ServerRecipeBook.LOGGER.error("Tried to load unrecognized recipe: {} removed now.", resourcekey); - } else { - handler.accept(resourcekey); -diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java b/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java -index 51ae8eddadc87b143b93521a3cef374f1e3a24dc..a7b45602d9d86aec235eef06d252fb6225f17f6e 100644 ---- a/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java -+++ b/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java -@@ -265,6 +265,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 - Improve logging and errors; 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 2ca6cc6ba72339459d9591a89995521a3c81d111..59b257051ed3040891f0a32b9c732f8bfabd6f7d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -240,4 +240,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/0024-Zombie-horse-naturally-spawn.patch b/patches/server/0024-Zombie-horse-naturally-spawn.patch deleted file mode 100644 index d09ab0b2b5..0000000000 --- a/patches/server/0024-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 4b9434b2d03cd24f5dac7098d2f1318fd12baddb..7ab130afddb0893016fb558ed624198316ca191d 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -940,10 +940,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - 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 - Configurable spawn chances for skeleton horses - - if (flag1) { -- SkeletonHorse entityhorseskeleton = (SkeletonHorse) EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT); -+ // Purpur start -+ net.minecraft.world.entity.animal.horse.AbstractHorse entityhorseskeleton; -+ if (purpurConfig.zombieHorseSpawnChance > 0D && random.nextDouble() <= purpurConfig.zombieHorseSpawnChance) { -+ entityhorseskeleton = EntityType.ZOMBIE_HORSE.create(this, EntitySpawnReason.EVENT); -+ } else { -+ entityhorseskeleton = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT); -+ 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 3c3df7767bc97882f53f32de1b4a350f73b16f23..7c7fce74431a52de80a29c0e4ca87b45e2ce892c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1657,6 +1657,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() { - zombieHorseRidable = getBoolean("mobs.zombie_horse.ridable", zombieHorseRidable); - zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater); -@@ -1673,6 +1674,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/0025-Charged-creeper-naturally-spawn.patch b/patches/server/0025-Charged-creeper-naturally-spawn.patch deleted file mode 100644 index d7c41946bc..0000000000 --- a/patches/server/0025-Charged-creeper-naturally-spawn.patch +++ /dev/null @@ -1,47 +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 a93b9bf974fdee49cf9fe814bfb86467971f2f53..d67136ccf5e23accb656f2532e2ced93669f2a8d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -258,6 +258,16 @@ public class Creeper extends Monster { - } - } - -+ // Purpur start - Charged creeper naturally spawn -+ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.EntitySpawnReason spawnReason, @Nullable net.minecraft.world.entity.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 - Charged creeper naturally spawn -+ - @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 7c7fce74431a52de80a29c0e4ca87b45e2ce892c..3ec1852cc481572e87204ca800edae94e6890e05 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 boolean creeperControllable = true; - public double creeperMaxHealth = 20.0D; - public double creeperScale = 1.0D; -+ public double creeperChargedChance = 0.0D; - private void creeperSettings() { - creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable); - creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater); -@@ -419,6 +420,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean dolphinRidable = false; diff --git a/patches/server/0026-Rabbit-naturally-spawn-toast-and-killer.patch b/patches/server/0026-Rabbit-naturally-spawn-toast-and-killer.patch deleted file mode 100644 index 2f26a42c58..0000000000 --- a/patches/server/0026-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 7ce438ad3083ff7f431c1f292d1c38d53c14672c..6448d4f44f948f08121302784e966bf58abd9be1 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -@@ -470,10 +470,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); - } - - 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 3ec1852cc481572e87204ca800edae94e6890e05..0ba770af96809403dd93317dcf5120c7d87bf587 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1105,6 +1105,8 @@ public class PurpurWorldConfig { - public boolean rabbitControllable = true; - public double rabbitMaxHealth = 3.0D; - public double rabbitScale = 1.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); -@@ -1116,6 +1118,8 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean ravagerRidable = false; diff --git a/patches/server/0027-Fix-outdated-server-showing-in-ping-before-server-fu.patch b/patches/server/0027-Fix-outdated-server-showing-in-ping-before-server-fu.patch deleted file mode 100644 index 3e74f14139..0000000000 --- a/patches/server/0027-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 532f09089b8d6798999cf3f83e852df7479e450e..43c63d203859eaa0999937e2f9254c22510139aa 100644 ---- a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java -@@ -154,6 +154,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/0028-Tulips-change-fox-type.patch b/patches/server/0028-Tulips-change-fox-type.patch deleted file mode 100644 index 5953115342..0000000000 --- a/patches/server/0028-Tulips-change-fox-type.patch +++ /dev/null @@ -1,80 +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 584d08bca961f8b8487b844cd2412b773401296d..a0adc29bb09a3eaecdaf564fd992b8aefec69629 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -385,6 +385,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.Variant.RED) { - this.targetSelector.addGoal(4, this.landTargetGoal); - this.targetSelector.addGoal(4, this.turtleEggTargetGoal); -@@ -414,6 +419,7 @@ public class Fox extends Animal implements VariantHolder { - - public void setVariant(Fox.Variant variant) { - this.entityData.set(Fox.DATA_TYPE_ID, variant.getId()); -+ this.setTargetGoals(); // Purpur - fix API bug not updating pathfinders on type change - } - - List getTrustedUUIDs() { -@@ -749,6 +755,29 @@ public class Fox extends Animal implements VariantHolder { - } - // Paper end - -+ // Purpur start -+ @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 -+ - @Override - // Paper start - Cancellable death event - protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(ServerLevel world, DamageSource damageSource) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0ba770af96809403dd93317dcf5120c7d87bf587..37f89fabc286ea07bf6438df7ed7c8d6bcb76d48 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -586,6 +586,7 @@ public class PurpurWorldConfig { - public boolean foxControllable = true; - public double foxMaxHealth = 10.0D; - public double foxScale = 1.0D; -+ public boolean foxTypeChangesWithTulips = false; - private void foxSettings() { - foxRidable = getBoolean("mobs.fox.ridable", foxRidable); - foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater); -@@ -597,6 +598,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean frogRidable = false; diff --git a/patches/server/0029-Breedable-Polar-Bears.patch b/patches/server/0029-Breedable-Polar-Bears.patch deleted file mode 100644 index daf3e7da58..0000000000 --- a/patches/server/0029-Breedable-Polar-Bears.patch +++ /dev/null @@ -1,84 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Fri, 12 Apr 2024 15:40:12 -0700 -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 1ec0f8a70e9e603def18fd90c84f4525f6985381..09039a5a69ea582223d6ad06b1d99776742ec2e2 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java -+++ b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java -@@ -100,6 +100,28 @@ public class PolarBear extends Animal implements NeutralMob { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.polarBearScale); - } - // Purpur end - Configurable entity base attributes -+ // 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 - @Nullable - @Override - public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) { -@@ -108,7 +130,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 - Breedable Polar Bears - } - - @Override -@@ -119,6 +141,12 @@ public class PolarBear extends Animal implements NeutralMob { - this.goalSelector.addGoal(1, new PolarBear.PolarBearMeleeAttackGoal()); - this.goalSelector - .addGoal(1, new PanicGoal(this, 2.0, polarBear -> polarBear.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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 37f89fabc286ea07bf6438df7ed7c8d6bcb76d48..efa453fdde735158b4c6324546ca83bdd2935746 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1073,6 +1073,8 @@ public class PurpurWorldConfig { - public boolean polarBearControllable = true; - public double polarBearMaxHealth = 30.0D; - public double polarBearScale = 1.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); -@@ -1084,6 +1086,9 @@ public class PurpurWorldConfig { - } - 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(ResourceLocation.parse(polarBearBreedableItemString)); -+ if (item != Items.AIR) polarBearBreedableItem = item; - } - - public boolean pufferfishRidable = false; diff --git a/patches/server/0030-Chickens-can-retaliate.patch b/patches/server/0030-Chickens-can-retaliate.patch deleted file mode 100644 index 6ed0f6ceea..0000000000 --- a/patches/server/0030-Chickens-can-retaliate.patch +++ /dev/null @@ -1,74 +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 2f1518536e63dfd94db5c8a2076004319408409c..f6f7afc6a421022738f77e5eece193ad15de78ba 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -75,13 +75,18 @@ 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 - @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.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, (itemstack) -> { - return itemstack.is(ItemTags.CHICKEN_FOOD); -@@ -90,6 +95,14 @@ public class Chicken 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)); -+ // 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 -@@ -98,7 +111,7 @@ public class Chicken extends Animal { - } - - public static AttributeSupplier.Builder createAttributes() { -- return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 4.0D).add(Attributes.MOVEMENT_SPEED, 0.25D); -+ return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 4.0D).add(Attributes.MOVEMENT_SPEED, 0.25D).add(Attributes.ATTACK_DAMAGE, 0.0D); // Purpur - Chickens can retaliate - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index efa453fdde735158b4c6324546ca83bdd2935746..dcc8e4880c1bfff1abdc1b542000d61e3378d4f1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -343,6 +343,7 @@ public class PurpurWorldConfig { - public boolean chickenControllable = true; - public double chickenMaxHealth = 4.0D; - public double chickenScale = 1.0D; -+ public boolean chickenRetaliate = false; - private void chickenSettings() { - chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable); - chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater); -@@ -354,6 +355,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean codRidable = false; diff --git a/patches/server/0031-Add-option-to-set-armorstand-step-height.patch b/patches/server/0031-Add-option-to-set-armorstand-step-height.patch deleted file mode 100644 index 70ee54153e..0000000000 --- a/patches/server/0031-Add-option-to-set-armorstand-step-height.patch +++ /dev/null @@ -1,55 +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 755861185bd8434027acca7f03ed0bfdf9fa2cde..fe5e8009d16fe9292312e37538b02b92b9fea9ed 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -300,6 +300,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - public double xOld; - public double yOld; - public double zOld; -+ public float maxUpStep; // Purpur - public boolean noPhysics; - private boolean wasOnFire; - public final RandomSource random; -@@ -5169,7 +5170,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - - public float maxUpStep() { -- return 0.0F; -+ return maxUpStep; - } - - public void onExplosionHit(@Nullable Entity entity) {} -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 70b8023c3badc745f342d5b0ab54699e3923826a..edb0cd90e28016c44b0aaf5c9ed5d7bdbced5295 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -681,6 +681,7 @@ public class ArmorStand extends LivingEntity { - - @Override - public void tick() { -+ maxUpStep = level().purpurConfig.armorstandStepHeight; - // Paper start - Allow ArmorStands not to tick - 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 dcc8e4880c1bfff1abdc1b542000d61e3378d4f1..149360f01139fa4b14e27e5b34fe3fe7682c7ffc 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -91,6 +91,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/0032-Cat-spawning-options.patch b/patches/server/0032-Cat-spawning-options.patch deleted file mode 100644 index 0c52801e5f..0000000000 --- a/patches/server/0032-Cat-spawning-options.patch +++ /dev/null @@ -1,71 +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 b0236c7bf9441aa84d3795ffed05dd6099f29636..e9cbbcdcefe9acc24cf7972ae356fd590e128f56 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java -+++ b/src/main/java/net/minecraft/world/entity/npc/CatSpawner.java -@@ -27,7 +27,7 @@ public class CatSpawner implements CustomSpawner { - if (this.nextTick > 0) { - return 0; - } else { -- this.nextTick = 1200; -+ this.nextTick = world.purpurConfig.catSpawnDelay; // Purpur - Cat spawning options - Player player = world.getRandomPlayer(); - if (player == null) { - return 0; -@@ -61,8 +61,12 @@ public class CatSpawner implements CustomSpawner { - - private int spawnInVillage(ServerLevel world, BlockPos pos) { - int i = 48; -- if (world.getPoiManager().getCountInRange(entry -> entry.is(PoiTypes.HOME), pos, 48, PoiManager.Occupancy.IS_OCCUPIED) > 4L) { -- List list = world.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(48.0, 8.0, 48.0)); -+ // Purpur start - Cat spawning options -+ int range = world.purpurConfig.catSpawnVillageScanRange; -+ if (range <= 0) return 0; -+ if (world.getPoiManager().getCountInRange(entry -> entry.is(PoiTypes.HOME), pos, range, PoiManager.Occupancy.IS_OCCUPIED) > 4L) { -+ List list = world.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(range, 8.0, range)); -+ // Purpur end - Cat spawning options - if (list.size() < 5) { - return this.spawnCat(pos, world); - } -@@ -73,7 +77,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.0, 8.0, 16.0)); -+ // Purpur start - Cat spawning options -+ int range = world.purpurConfig.catSpawnSwampHutScanRange; -+ if (range <= 0) return 0; -+ List list = world.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(range, 8.0, range)); -+ // Purpur end - Cat spawning options - 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 149360f01139fa4b14e27e5b34fe3fe7682c7ffc..b3284dfbcdc240919776f47a780a824c406c2781 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -312,6 +312,9 @@ public class PurpurWorldConfig { - 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; - private void catSettings() { - catRidable = getBoolean("mobs.cat.ridable", catRidable); - catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater); -@@ -323,6 +326,9 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean caveSpiderRidable = false; diff --git a/patches/server/0034-Fix-cow-rotation-when-shearing-mooshroom.patch b/patches/server/0034-Fix-cow-rotation-when-shearing-mooshroom.patch deleted file mode 100644 index d000872059..0000000000 --- a/patches/server/0034-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 215ec83077a1d40fdcee5bf4cd1af0d46fdd695e..96078cf11f13c85a3f019f70fa02668c43dbc559 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -219,6 +219,13 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { - world.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(0.5D), this.getZ(), 1, 0.0D, 0.0D, 0.0D, 0.0D); -+ // Purpur start -+ entitycow.copyPosition(this); -+ entitycow.yBodyRot = this.yBodyRot; -+ entitycow.setYHeadRot(this.getYHeadRot()); -+ entitycow.yRotO = this.yRotO; -+ entitycow.xRotO = this.xRotO; -+ // Purpur end - // Paper start - custom shear drops; moved drop generation to separate method - drops.forEach(drop -> { - ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY(1.0D), this.getZ(), drop); diff --git a/patches/server/0035-Pigs-give-saddle-back.patch b/patches/server/0035-Pigs-give-saddle-back.patch deleted file mode 100644 index 7d27a08ce0..0000000000 --- a/patches/server/0035-Pigs-give-saddle-back.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -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 b7e4e4bbb85a44e0f2cd49a1419216ca3c526cd7..f9efd4c1fc30f2bbb061d31ded6e45c546b0bfa8 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Pig.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Pig.java -@@ -181,6 +181,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 6eb25a9b4bb634e57f6de015c9a9f2fc78d0b86f..419ebb012056d131d6d1b10c8bd72511a1c4df74 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1016,6 +1016,7 @@ public class PurpurWorldConfig { - public boolean pigControllable = true; - public double pigMaxHealth = 10.0D; - public double pigScale = 1.0D; -+ public boolean pigGiveSaddleBack = false; - private void pigSettings() { - pigRidable = getBoolean("mobs.pig.ridable", pigRidable); - pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater); -@@ -1027,6 +1028,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean piglinRidable = false; diff --git a/patches/server/0036-Snowman-drop-and-put-back-pumpkin.patch b/patches/server/0036-Snowman-drop-and-put-back-pumpkin.patch deleted file mode 100644 index d2456957e5..0000000000 --- a/patches/server/0036-Snowman-drop-and-put-back-pumpkin.patch +++ /dev/null @@ -1,45 +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 0149071cc901beffd583c1ba7617e1355473fc77..aaa581c5fa7a14958b2a7c20996cf70ad4efff04 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -205,6 +205,14 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - } - - return InteractionResult.SUCCESS; -+ // 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 - Ridables - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 419ebb012056d131d6d1b10c8bd72511a1c4df74..1fa34a6517b4b834354234471f9ed3bcaaf1ee03 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1311,6 +1311,7 @@ public class PurpurWorldConfig { - public boolean snowGolemLeaveTrailWhenRidden = false; - public double snowGolemMaxHealth = 4.0D; - public double snowGolemScale = 1.0D; -+ public boolean snowGolemPutPumpkinBack = false; - private void snowGolemSettings() { - snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable); - snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater); -@@ -1323,6 +1324,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean snifferRidable = false; diff --git a/patches/server/0037-Ender-dragon-always-drop-full-exp.patch b/patches/server/0037-Ender-dragon-always-drop-full-exp.patch deleted file mode 100644 index 908b522b7d..0000000000 --- a/patches/server/0037-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 1e1cd061203ab3ca2f1feb4a3f9fe2813d3d96aa..14feab288349f0fcd80b89466926b37d1087bf53 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 -@@ -733,7 +733,7 @@ public class EnderDragon extends Mob implements Enemy { - boolean flag = worldserver.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 1fa34a6517b4b834354234471f9ed3bcaaf1ee03..a21ced5b26dd176863d26c90c5aa083e64f3cd6a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -525,6 +525,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); -@@ -540,6 +541,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/0038-Allow-soil-to-moisten-from-water-directly-under-it.patch b/patches/server/0038-Allow-soil-to-moisten-from-water-directly-under-it.patch deleted file mode 100644 index ad5151bfc9..0000000000 --- a/patches/server/0038-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 c3dba0c2c94f3804338f86621dc42405e380a6b3..df89b18f0d5d2dad5745ec65bcd9e4a7bf2f1f2b 100644 ---- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -@@ -174,7 +174,7 @@ public class FarmBlock extends Block { - } - } - -- return false; -+ return ((ServerLevel) world).purpurConfig.farmlandGetsMoistFromBelow && world.getFluidState(pos.relative(Direction.DOWN)).is(FluidTags.WATER); // Purpur; - // Paper end - Perf: remove abstract block iteration - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a21ced5b26dd176863d26c90c5aa083e64f3cd6a..1cc5c2f6b2d110174a2c79d26386f96301050834 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -146,6 +146,11 @@ public class PurpurWorldConfig { - }); - } - -+ public boolean farmlandGetsMoistFromBelow = false; -+ private void farmlandSettings() { -+ farmlandGetsMoistFromBelow = getBoolean("blocks.farmland.gets-moist-from-below", farmlandGetsMoistFromBelow); -+ } -+ - public boolean turtleEggsBreakFromExpOrbs = false; - public boolean turtleEggsBreakFromItems = false; - public boolean turtleEggsBreakFromMinecarts = false; diff --git a/patches/server/0039-Minecart-settings-and-WASD-controls.patch b/patches/server/0039-Minecart-settings-and-WASD-controls.patch deleted file mode 100644 index d7fc7a048e..0000000000 --- a/patches/server/0039-Minecart-settings-and-WASD-controls.patch +++ /dev/null @@ -1,236 +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 5e8a5e8e4120420a170c4733ae217e7948cef46c..3ea226168519e07b10d015f4c9b2b369569daf11 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1439,6 +1439,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - if (this.isInvulnerableTo(world, source)) { - return false; - } else { -+ if (source.is(net.minecraft.tags.DamageTypeTags.IS_FALL) && getRootVehicle() instanceof net.minecraft.world.entity.vehicle.AbstractMinecart && level().purpurConfig.minecartControllable && !level().purpurConfig.minecartControllableFallDamage) return false; // Purpur - Minecart settings and WASD controls - Entity entity = source.getEntity(); - - if (entity instanceof net.minecraft.world.entity.player.Player) { -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 cdc8606ffe5c75ee19d92e9f86f26b2a502d765e..d0c93f87795d7162bbfb3fdadadb6ae1c5fdaeeb 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -@@ -92,6 +92,10 @@ public abstract class AbstractMinecart extends VehicleEntity { - private double flyingY = 0.95; - private double flyingZ = 0.95; - public Double maxSpeed; -+ // Purpur start - Minecart settings and WASD controls -+ public double storedMaxSpeed; -+ public boolean isNewBehavior; -+ // Purpur end - Minecart settings and WASD controls - // CraftBukkit end - public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API - -@@ -100,8 +104,13 @@ public abstract class AbstractMinecart extends VehicleEntity { - this.blocksBuilding = true; - if (AbstractMinecart.useExperimentalMovement(world)) { - 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 = world.purpurConfig.minecartMaxSpeed; -+ // Purpur end - Minecart settings and WASD controls - } - - } -@@ -289,6 +298,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(); -@@ -426,16 +443,63 @@ public abstract class AbstractMinecart extends VehicleEntity { - this.behavior.moveAlongTrack(world); - } - -+ // 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 world) { - double d0 = this.getMaxSpeed(world); - Vec3 vec3d = this.getDeltaMovement(); - - this.setDeltaMovement(Mth.clamp(vec3d.x, -d0, d0), vec3d.y, Mth.clamp(vec3d.z, -d0, d0)); -+ -+ // 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) { -+ 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/src/main/java/net/minecraft/world/item/MinecartItem.java b/src/main/java/net/minecraft/world/item/MinecartItem.java -index 7153d9ed12276a0f2d8b8a17c79734aa25ed1fa5..a82faf7f07cd71c8748979a726b1e7857d88e195 100644 ---- a/src/main/java/net/minecraft/world/item/MinecartItem.java -+++ b/src/main/java/net/minecraft/world/item/MinecartItem.java -@@ -35,8 +35,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; // Purpur - Minecart settings and WASD controls -+ if (iblockdata.isSolid()) blockposition = blockposition.relative(context.getClickedFace()); -+ } // else { // Purpur - Minecart settings and WASD controls - ItemStack itemstack = context.getItemInHand(); - RailShape blockpropertytrackposition = iblockdata.getBlock() instanceof BaseRailBlock ? (RailShape) iblockdata.getValue(((BaseRailBlock) iblockdata.getBlock()).getShapeProperty()) : RailShape.NORTH_SOUTH; - double d0 = 0.0D; -@@ -80,6 +81,6 @@ public class MinecartItem extends Item { - itemstack.shrink(1); - return InteractionResult.SUCCESS; - } -- } -+ // } // Purpur - Minecart settings and WASD controls - } - } -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 8c0f332a1a0918f60226d969918ae7fe4fe74166..6376b8b3ff444f4cab93e2bb5d2becc77c33c118 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 -@@ -99,7 +99,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 - protected final Optional> drops; - protected final String descriptionId; - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1cc5c2f6b2d110174a2c79d26386f96301050834..21e3931d64d32e69b8ba6f7e6d1a9bf044c7c9a8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -96,6 +96,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.getValue(ResourceLocation.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); -+ } -+ } -+ - public boolean idleTimeoutKick = true; - public boolean idleTimeoutTickNearbyEntities = true; - public boolean idleTimeoutCountAsSleeping = false; diff --git a/patches/server/0040-Disable-loot-drops-on-death-by-cramming.patch b/patches/server/0040-Disable-loot-drops-on-death-by-cramming.patch deleted file mode 100644 index e0861d9ea4..0000000000 --- a/patches/server/0040-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 6884475a5217acb8073dc38e5f7a7b341c8b1dd1..34f2c17831c6edd16b6dc32f3d344b3c209244fc 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1925,6 +1925,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - - this.dropEquipment(world); // CraftBukkit - from below - if (this.shouldDropLoot() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { -+ if (!(damageSource.is(net.minecraft.world.damagesource.DamageTypes.CRAMMING) && level().purpurConfig.disableDropsOnCrammingDeath)) { // Purpur - this.dropFromLootTable(world, damageSource, flag); - // Paper start - final boolean prev = this.clearEquipmentSlots; -@@ -1933,6 +1934,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - // Paper end - this.dropCustomDeathLoot(world, damageSource, 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, damageSource, this.drops, () -> { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 21e3931d64d32e69b8ba6f7e6d1a9bf044c7c9a8..983523dfff2773799640347ff047e600fb25044e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -96,6 +96,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/0041-Option-to-toggle-milk-curing-bad-omen.patch b/patches/server/0041-Option-to-toggle-milk-curing-bad-omen.patch deleted file mode 100644 index 881bdbc1d0..0000000000 --- a/patches/server/0041-Option-to-toggle-milk-curing-bad-omen.patch +++ /dev/null @@ -1,38 +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/consume_effects/ClearAllStatusEffectsConsumeEffect.java b/src/main/java/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java -index 0651c2af040e3f248860cfb3c5effce91589380e..d884df481b4bbb978113a4ac7a1feac31cf2f951 100644 ---- a/src/main/java/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java -+++ b/src/main/java/net/minecraft/world/item/consume_effects/ClearAllStatusEffectsConsumeEffect.java -@@ -24,6 +24,12 @@ public record ClearAllStatusEffectsConsumeEffect() implements ConsumeEffect { - @Override - // CraftBukkit start - public boolean apply(Level world, ItemStack itemstack, LivingEntity entityliving, EntityPotionEffectEvent.Cause cause) { -+ // Purpur start -+ net.minecraft.world.effect.MobEffectInstance badOmen = entityliving.getEffect(net.minecraft.world.effect.MobEffects.BAD_OMEN); -+ if (!world.purpurConfig.milkCuresBadOmen && itemstack.is(net.minecraft.world.item.Items.MILK_BUCKET) && badOmen != null) { -+ return entityliving.removeAllEffects(cause) && entityliving.addEffect(badOmen); -+ } -+ // Purpur end - return entityliving.removeAllEffects(cause); - // CraftBukkit end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 983523dfff2773799640347ff047e600fb25044e..6daa99bb60723af0eb38b625ab0cd24d33d7552a 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 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/0042-Skip-events-if-there-s-no-listeners.patch b/patches/server/0042-Skip-events-if-there-s-no-listeners.patch deleted file mode 100644 index 230c1bd2ca..0000000000 --- a/patches/server/0042-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 517cb238ec280aadd1fc54bcb675ed386e798eaf..832930024b0d642496efe17f7e2f8e02413275f0 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -515,6 +515,7 @@ public class Commands { - private void runSync(ServerPlayer player, 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 (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); - -@@ -525,6 +526,7 @@ public class Commands { - } - } - // CraftBukkit end -+ } // Purpur - skip event - player.connection.send(new ClientboundCommandsPacket(rootcommandnode)); - } - diff --git a/patches/server/0043-Add-permission-for-F3-N-debug.patch b/patches/server/0043-Add-permission-for-F3-N-debug.patch deleted file mode 100644 index 07272b51e5..0000000000 --- a/patches/server/0043-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 b9136fcec3f36549ddd572cbb8cd01ecae45590f..8596e1c016c51a840b8c0c6a8d5c8e0893c4f1c7 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -1108,6 +1108,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/0044-Configurable-TPS-Catchup.patch b/patches/server/0044-Configurable-TPS-Catchup.patch deleted file mode 100644 index 89e37d0253..0000000000 --- a/patches/server/0044-Configurable-TPS-Catchup.patch +++ /dev/null @@ -1,37 +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 18dc66b78f91c0b9efdf9008c0a38c52fdfc93ad..22cbe00a19a2ac0ad25b1107b1290b793903e3aa 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1344,6 +1344,12 @@ 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 1e045397763e8233c2e8f9955468788374b80068..3573a6cf0632dfb6859f5a5e7ceffb947e9d3692 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java -@@ -70,7 +70,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()) { - Level world = this.level(); - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 6daa99bb60723af0eb38b625ab0cd24d33d7552a..bdf391eda89bc061353c0fd72ab5a9c5fe85e55e 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 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/0046-Add-enderman-and-creeper-griefing-controls.patch b/patches/server/0046-Add-enderman-and-creeper-griefing-controls.patch deleted file mode 100644 index 1a4a3e298d..0000000000 --- a/patches/server/0046-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 d67136ccf5e23accb656f2532e2ced93669f2a8d..4711f5e5d9f1738d739be68f586f744eba8621c9 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -365,7 +365,7 @@ public class Creeper extends Monster { - if (!event.isCancelled()) { - // CraftBukkit end - this.dead = true; -- worldserver.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) -+ worldserver.explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), worldserver.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING) && 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(worldserver, Entity.RemovalReason.KILLED); - this.discard(EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause -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 9f7261bd22803f0b094e2bfd4a0eb66ad727211e..13eaf1e8b12c3b4cba6b202f2d31e99a8a9bae92 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -514,6 +514,7 @@ public class EnderMan extends Monster implements NeutralMob { - - @Override - public boolean canUse() { -+ if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - Add enderman and creeper griefing controls - return this.enderman.getCarriedBlock() == null ? false : (!getServerLevel((Entity) this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0); - } - -@@ -559,6 +560,7 @@ public class EnderMan extends Monster implements NeutralMob { - - @Override - public boolean canUse() { -+ if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - Add enderman and creeper griefing controls - return this.enderman.getCarriedBlock() != null ? false : (!getServerLevel((Entity) this.enderman).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 bdf391eda89bc061353c0fd72ab5a9c5fe85e55e..b33ab2fc455c212284ef50cfecc778000741ead5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -500,6 +500,7 @@ public class PurpurWorldConfig { - public double creeperMaxHealth = 20.0D; - public double creeperScale = 1.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); -@@ -512,6 +513,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean dolphinRidable = false; -@@ -625,6 +627,7 @@ public class PurpurWorldConfig { - public boolean endermanControllable = true; - public double endermanMaxHealth = 40.0D; - public double endermanScale = 1.0D; -+ public boolean endermanAllowGriefing = true; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -@@ -636,6 +639,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean endermiteRidable = false; diff --git a/patches/server/0047-Villagers-follow-emerald-blocks.patch b/patches/server/0047-Villagers-follow-emerald-blocks.patch deleted file mode 100644 index baeccdd1de..0000000000 --- a/patches/server/0047-Villagers-follow-emerald-blocks.patch +++ /dev/null @@ -1,140 +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/attributes/DefaultAttributes.java b/src/main/java/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java -index 8ccbf0386aa453e82fc0f82d2aefd1e08b6c3345..8c1ff0b273e25373c6b581613f692265d9569952 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 -@@ -162,7 +162,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.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/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/TemptGoal.java -index 84ab90dd1fe693da71732533ccff940c1007e1b6..798d1a169ebebfbdc5e0cf71e7a1256cefcbd32b 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 -@@ -67,7 +67,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 - Villagers follow emerald blocks - } - - @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 3c037cabd8331eb96a6523b37abab4e73ab79a02..94ba68e063e63f77f7951d482af7a3586ba4ffdf 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java -@@ -48,6 +48,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 - Villagers follow emerald blocks - - // CraftBukkit start - @Override -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 39f2091391222bcd4ab0f2677f896c5ecdc6e86c..129833b7ecd644e4180fadca15015276961725c6 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -175,6 +175,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 - -@@ -183,6 +184,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 - @Override -@@ -246,7 +248,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - } - - public static AttributeSupplier.Builder createAttributes() { -- return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.5D); -+ return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.5D).add(Attributes.TEMPT_RANGE, 10.0D); // Purpur - Villagers follow emerald blocks - } - - public boolean assignProfessionWhenSpawned() { -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 8e12ae313c76d742b61aa83a35d41d83b92bded5..c307aa1153d6a17ee8624453fc9303ac5749b6ef 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -93,8 +93,14 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - @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)); -@@ -115,6 +121,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 - Villagers follow emerald blocks - 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 b33ab2fc455c212284ef50cfecc778000741ead5..b81790ec5a7e4b73fbef52229844506b072dfa0d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1588,6 +1588,8 @@ public class PurpurWorldConfig { - public boolean villagerControllable = true; - public double villagerMaxHealth = 20.0D; - public double villagerScale = 1.0D; -+ public boolean villagerFollowEmeraldBlock = false; -+ public double villagerTemptRange = 10.0D; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -1599,6 +1601,8 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean vindicatorRidable = false; -@@ -1624,6 +1628,8 @@ public class PurpurWorldConfig { - public boolean wanderingTraderControllable = true; - public double wanderingTraderMaxHealth = 20.0D; - public double wanderingTraderScale = 1.0D; -+ public boolean wanderingTraderFollowEmeraldBlock = false; -+ public double wanderingTraderTemptRange = 10.0D; - private void wanderingTraderSettings() { - wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable); - wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater); -@@ -1635,6 +1641,8 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean wardenRidable = false; diff --git a/patches/server/0048-Allow-leashing-villagers.patch b/patches/server/0048-Allow-leashing-villagers.patch deleted file mode 100644 index 88a753be4d..0000000000 --- a/patches/server/0048-Allow-leashing-villagers.patch +++ /dev/null @@ -1,88 +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/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index fe5e8009d16fe9292312e37538b02b92b9fea9ed..7b017806dde98a33b64223f098cc63c286dac02b 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3115,6 +3115,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - if (this.isAlive() && this instanceof Leashable leashable) { - if (leashable.getLeashHolder() == player) { - if (!this.level().isClientSide()) { -+ if (hand == InteractionHand.OFF_HAND && (level().purpurConfig.villagerCanBeLeashed || level().purpurConfig.wanderingTraderCanBeLeashed) && this instanceof net.minecraft.world.entity.npc.AbstractVillager) return InteractionResult.CONSUME; // Purpur - Allow leashing villagers - // CraftBukkit start - fire PlayerUnleashEntityEvent - // Paper start - Expand EntityUnleashEvent - org.bukkit.event.player.PlayerUnleashEntityEvent event = CraftEventFactory.callPlayerUnleashEntityEvent(this, player, hand, !player.hasInfiniteMaterials()); -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 129833b7ecd644e4180fadca15015276961725c6..f89c82db6333dc64ac57e225f5522943eb959f46 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -187,6 +187,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - this.getAttribute(Attributes.TEMPT_RANGE).setBaseValue(this.level().purpurConfig.villagerTemptRange); // Purpur - Villagers follow emerald blocks - } - // Purpur end - Configurable entity base attributes -+ // Purpur start - Allow leashing villagers -+ @Override -+ public boolean canBeLeashed() { -+ return level().purpurConfig.villagerCanBeLeashed; -+ } -+ // Purpur end - Allow leashing villagers - @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 c307aa1153d6a17ee8624453fc9303ac5749b6ef..0066e413d1fa92ecd5573c00195aa2f1412e665e 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -101,6 +101,12 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - return Mob.createMobAttributes().add(net.minecraft.world.entity.ai.attributes.Attributes.TEMPT_RANGE, 10.0D); - } - // Purpur end - Villagers follow emerald blocks -+ // 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)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b81790ec5a7e4b73fbef52229844506b072dfa0d..1b96a84a1c81c075aa5f7308b89d396a4578be33 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1590,6 +1590,7 @@ public class PurpurWorldConfig { - public double villagerScale = 1.0D; - public boolean villagerFollowEmeraldBlock = false; - public double villagerTemptRange = 10.0D; -+ public boolean villagerCanBeLeashed = false; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -1603,6 +1604,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean vindicatorRidable = false; -@@ -1630,6 +1632,7 @@ public class PurpurWorldConfig { - public double wanderingTraderScale = 1.0D; - public boolean wanderingTraderFollowEmeraldBlock = false; - public double wanderingTraderTemptRange = 10.0D; -+ public boolean wanderingTraderCanBeLeashed = false; - private void wanderingTraderSettings() { - wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable); - wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater); -@@ -1643,6 +1646,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean wardenRidable = false; diff --git a/patches/server/0049-Implement-infinite-liquids.patch b/patches/server/0049-Implement-infinite-liquids.patch deleted file mode 100644 index 280ecf911f..0000000000 --- a/patches/server/0049-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 f4fbcbb8ff6d2677af1a02a0801a323c06dce9b1..6f024a29e8824a604ff0c74f88522d23423beb5c 100644 ---- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -@@ -266,7 +266,7 @@ public abstract class FlowingFluid extends Fluid { - } - } - -- if (j >= 2 && this.canConvertToSource(world)) { -+ if (j >= getRequiredSources(world) && this.canConvertToSource(world)) { // Purpur - BlockState iblockdata2 = world.getBlockState(blockposition_mutableblockposition.setWithOffset(pos, Direction.DOWN)); - FluidState fluid1 = iblockdata2.getFluidState(); - -@@ -356,6 +356,12 @@ public abstract class FlowingFluid extends Fluid { - - protected abstract boolean canConvertToSource(ServerLevel 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) { - Block block = state.getBlock(); - -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 884db3e64cb22ed765beec8f11ea309fcf810207..e9687c3580e5fff33ce902eb7b86eb815ca1072d 100644 ---- a/src/main/java/net/minecraft/world/level/material/LavaFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/LavaFluid.java -@@ -199,6 +199,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(ServerLevel 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 552925ba47c7475e2e1ec2ded0966f28ed3e50a5..1e741f36b79585f33abe413beafe00cf5205d54f 100644 ---- a/src/main/java/net/minecraft/world/level/material/WaterFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/WaterFluid.java -@@ -81,6 +81,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 - Add BlockBreakBlockEvent - @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 1b96a84a1c81c075aa5f7308b89d396a4578be33..8221f383726a4d6aec895529f9330c64de286a35 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -222,6 +222,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 = false; - public boolean turtleEggsBreakFromItems = false; - public boolean turtleEggsBreakFromMinecarts = false; -@@ -231,6 +236,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/0050-Make-lava-flow-speed-configurable.patch b/patches/server/0050-Make-lava-flow-speed-configurable.patch deleted file mode 100644 index b3d3a8b64b..0000000000 --- a/patches/server/0050-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 e9687c3580e5fff33ce902eb7b86eb815ca1072d..6e643c1a7f7e71cfd20603facaf224985ee81716 100644 ---- a/src/main/java/net/minecraft/world/level/material/LavaFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/LavaFluid.java -@@ -181,7 +181,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 8221f383726a4d6aec895529f9330c64de286a35..8b2d9b592ef0f90df0c623595fb425e4f37d0c04 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -223,8 +223,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 = false; diff --git a/patches/server/0051-Add-player-death-exp-control-options.patch b/patches/server/0051-Add-player-death-exp-control-options.patch deleted file mode 100644 index ee7bc72069..0000000000 --- a/patches/server/0051-Add-player-death-exp-control-options.patch +++ /dev/null @@ -1,57 +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 2f69a511db8d43fbd3a17387cded1d3573579fce..8ccc760e84aec8aabbe5b8112b332cee13849b23 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1924,7 +1924,23 @@ public abstract class Player extends LivingEntity { - - @Override - protected int getBaseExperienceReward(ServerLevel world) { -- return !world.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !this.isSpectator() ? Math.min(this.experienceLevel * 7, 100) : 0; -+ // Purpur start -+ if (!world.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !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 - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 8b2d9b592ef0f90df0c623595fb425e4f37d0c04..f62000475acb9e48f3c86c6ed267ed3b2038529a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -172,6 +172,8 @@ public class PurpurWorldConfig { - public boolean idleTimeoutCountAsSleeping = false; - public boolean idleTimeoutUpdateTabList = false; - public boolean idleTimeoutTargetPlayer = true; -+ 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); -@@ -183,6 +185,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); -+ 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/0053-Add-canSaveToDisk-to-Entity.patch b/patches/server/0053-Add-canSaveToDisk-to-Entity.patch deleted file mode 100644 index fa153cc4dc..0000000000 --- a/patches/server/0053-Add-canSaveToDisk-to-Entity.patch +++ /dev/null @@ -1,87 +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 7b017806dde98a33b64223f098cc63c286dac02b..eea1053e0ec1446e117d3ac4732deaba191db8f2 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -569,6 +569,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - } - // 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 world) { - this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -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 8296765d8f63f1a9fd207b27d495d7c04646f134..45e0b9b3653de2dcb51a579f939b991beac03149 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -@@ -122,6 +122,12 @@ public class WitherSkull extends AbstractHurtingProjectile { - return target != this.getRider() && super.canHitEntity(target); - } - // Purpur end - Ridables -+ // 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/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java -index 356d010506fd21f3c752e4aa86c46c1106fdde3b..8573d4dbb45db6510d1a4deccb3e5a257504f7d5 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 -@@ -106,6 +106,7 @@ public class EntityStorage implements EntityPersistentStorage { - } - // Paper end - Entity load/save limit per chunk - CompoundTag compoundTagx = new CompoundTag(); -+ if (!entity.canSaveToDisk()) return; // Purpur - Add canSaveToDisk to Entity - if (entity.save(compoundTagx)) { - listTag.add(compoundTagx); - } -diff --git a/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java -index e33e54fc31ab7dcff054d0ab245d6c3391d06449..477ae2cd8ded25023976e3b7525e0c3b0e8259d9 100644 ---- a/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java -+++ b/src/main/java/org/purpurmc/purpur/entity/DolphinSpit.java -@@ -36,6 +36,13 @@ public class DolphinSpit extends LlamaSpit { - dolphin.getZ() + (double) (dolphin.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(dolphin.yBodyRot * 0.017453292F)); - } - -+ // Purpur start - Add canSaveToDisk to Entity -+ @Override -+ public boolean canSaveToDisk() { -+ return false; -+ } -+ // Purpur end - Add canSaveToDisk to Entity -+ - 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 3759e45afe16bf1d8a37b78d3526ee446e63cfe5..f57d77c0cab0174e67c1fdda6ac56f408ad6a902 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 - Add canSaveToDisk to Entity -+ @Override -+ public boolean canSaveToDisk() { -+ return false; -+ } -+ // Purpur end - Add canSaveToDisk to Entity -+ - public void tick() { - super_tick(); - diff --git a/patches/server/0054-Dispenser-curse-of-binding-protection.patch b/patches/server/0054-Dispenser-curse-of-binding-protection.patch deleted file mode 100644 index fb198a7fda..0000000000 --- a/patches/server/0054-Dispenser-curse-of-binding-protection.patch +++ /dev/null @@ -1,61 +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/core/dispenser/EquipmentDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java -index bf8c511739265c6a9cd277752e844481598f8966..ffe2399ab6b1f311536475d8216238b5b01c5dab 100644 ---- a/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java -+++ b/src/main/java/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java -@@ -41,7 +41,7 @@ public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior { - return false; - } else { - LivingEntity entityliving = (LivingEntity) list.getFirst(); -- EquipmentSlot enumitemslot = entityliving.getEquipmentSlotForItem(stack); -+ EquipmentSlot enumitemslot = pointer.level().purpurConfig.dispenserApplyCursedArmor ? entityliving.getEquipmentSlotForItem(stack) : entityliving.getEquipmentSlotForDispenserItem(stack); if (enumitemslot == null) return false; // Purpur - Dispenser curse of binding protection - ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event - - // CraftBukkit start -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 34f2c17831c6edd16b6dc32f3d344b3c209244fc..8829e613e1c307e9a40b30a797cada500971453d 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -4699,7 +4699,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - if (equippable != null && equippable.dispensable()) { - EquipmentSlot enumitemslot = equippable.slot(); - -- return this.canUseSlot(enumitemslot) && equippable.canBeEquippedBy(this.getType()) ? this.getItemBySlot(enumitemslot).isEmpty() && this.canDispenserEquipIntoSlot(enumitemslot) : false; -+ return this.canUseSlot(enumitemslot) && equippable.canBeEquippedBy(this.getType()) && this.getItemBySlot(enumitemslot).isEmpty() && this.canDispenserEquipIntoSlot(enumitemslot); - } else { - return false; - } -@@ -4724,6 +4724,12 @@ public abstract class LivingEntity extends Entity implements Attackable { - return equippable == null ? 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 ? SlotAccess.forEquipmentSlot(entity, slot, (itemstack) -> { - return itemstack.isEmpty() || entity.getEquipmentSlotForItem(itemstack) == slot; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f62000475acb9e48f3c86c6ed267ed3b2038529a..04b3b0661143d4d600dd34cd91498acbc929b10d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -221,6 +221,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/0055-Add-option-for-boats-to-eject-players-on-land.patch b/patches/server/0055-Add-option-for-boats-to-eject-players-on-land.patch deleted file mode 100644 index a3c74c910e..0000000000 --- a/patches/server/0055-Add-option-for-boats-to-eject-players-on-land.patch +++ /dev/null @@ -1,35 +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/AbstractBoat.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java -index b87fd952020d88a6612429df73121422bf9ae422..a301f32a292afb010f3888732e339897b176243d 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java -@@ -499,6 +499,7 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable { - - if (f > 0.0F) { - this.landFriction = f; -+ if (level().purpurConfig.boatEjectPlayersOnLand) ejectPassengers(); // Purpur - return AbstractBoat.Status.ON_LAND; - } else { - return AbstractBoat.Status.IN_AIR; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 04b3b0661143d4d600dd34cd91498acbc929b10d..3e78e57f29e98db2dde7d650b65ee23f99238ff7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -96,10 +96,12 @@ 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; - 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/0056-Mending-mends-most-damages-equipment-first.patch b/patches/server/0056-Mending-mends-most-damages-equipment-first.patch deleted file mode 100644 index 479b5475b7..0000000000 --- a/patches/server/0056-Mending-mends-most-damages-equipment-first.patch +++ /dev/null @@ -1,129 +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 23b47d90fd35659d9eaa661e808a7f89b29614cf..f1eb74a43881400f23f3750c909f999b35c2f5ce 100644 ---- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -+++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -@@ -353,7 +353,7 @@ public class ExperienceOrb extends Entity { - } - - private int repairPlayerItems(ServerPlayer player, int amount) { -- Optional optional = EnchantmentHelper.getRandomItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player, ItemStack::isDamaged); -+ Optional optional = level().purpurConfig.useBetterMending ? EnchantmentHelper.getMostDamagedItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player) : EnchantmentHelper.getRandomItemWith(EnchantmentEffectComponents.REPAIR_WITH_XP, player, ItemStack::isDamaged); // Purpur - Add option to mend the most damaged equipment first - - if (optional.isPresent()) { - ItemStack itemstack = ((EnchantedItemInUse) optional.get()).itemStack(); -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index 731d2bf4a5861bfe10496162d367e003f40b2d50..122859ba52e9719f98111a9c543dab27abac39b5 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -686,6 +686,26 @@ public final class ItemStack implements DataComponentHolder { - 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((Integer) this.getOrDefault(DataComponents.DAMAGE, 0), 0, this.getMaxDamage()); - } -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 64c315372277300e58ce413210f47eed5b6e1d6c..2c1799e8c9009a6ab24c24e7363d5a87e41f0c35 100644 ---- a/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java -+++ b/src/main/java/net/minecraft/world/item/enchantment/EnchantmentHelper.java -@@ -6,6 +6,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry; - import java.util.ArrayList; - import java.util.Collection; - import java.util.List; -+import java.util.Map; - import java.util.Optional; - import java.util.function.BiConsumer; - import java.util.function.Consumer; -@@ -588,4 +589,48 @@ public class EnchantmentHelper { - 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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 3e78e57f29e98db2dde7d650b65ee23f99238ff7..6b7a6a7889353f9557c6e3bfa8ff8caf66416c1b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -96,11 +96,13 @@ 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; - public double tridentLoyaltyVoidReturnHeight = 0.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/0057-Add-5-second-tps-average-in-tps.patch b/patches/server/0057-Add-5-second-tps-average-in-tps.patch deleted file mode 100644 index b2979597ae..0000000000 --- a/patches/server/0057-Add-5-second-tps-average-in-tps.patch +++ /dev/null @@ -1,108 +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 12b327eea95e0de9e9c39b7d039badee8ec46508..46696cfe1d99e705d383a1fe4e66f5c5646053d2 100644 ---- a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java -+++ b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java -@@ -61,6 +61,7 @@ public class RAMDetails extends JList { - - // Follows CraftServer#getTPS - double[] tps = new double[] { -+ server.tps5s.getAverage(), // Purpur - Add 5 second tps average in /tps - server.tps1.getAverage(), - server.tps5.getAverage(), - server.tps15.getAverage() -@@ -73,7 +74,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((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/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 6cec325942c44da6455f0b349ad83e1dc2b8b7d4..75ef1a4f80449e87affbd9ba62f5f9447ef9db87 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -324,7 +324,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0 && args[0].equals("mem") && sender.hasPermission("bukkit.command.tpsmemory")) { diff --git a/patches/server/0058-Implement-elytra-settings.patch b/patches/server/0058-Implement-elytra-settings.patch deleted file mode 100644 index db5d39d483..0000000000 --- a/patches/server/0058-Implement-elytra-settings.patch +++ /dev/null @@ -1,136 +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 8829e613e1c307e9a40b30a797cada500971453d..d711bef21fba8e32ae99a0a52992e609e4841734 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -3772,7 +3772,17 @@ public abstract class LivingEntity extends Entity implements Attackable { - }).toList(); - EquipmentSlot enumitemslot = (EquipmentSlot) Util.getRandom(list, this.random); - -- this.getItemBySlot(enumitemslot).hurtAndBreak(1, this, enumitemslot); -+ // Purpur start -+ int damage = level().purpurConfig.elytraDamagePerSecond; -+ if (level().purpurConfig.elytraDamageMultiplyBySpeed > 0) { -+ double speed = getDeltaMovement().lengthSqr(); -+ if (speed > level().purpurConfig.elytraDamageMultiplyBySpeed) { -+ damage *= (int) speed; -+ } -+ } -+ -+ this.getItemBySlot(enumitemslot).hurtAndBreak(damage, this, enumitemslot); -+ // Purpur end - } - - this.gameEvent(GameEvent.ELYTRA_GLIDE); -@@ -3781,7 +3791,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - - } - -- protected boolean canGlide() { -+ public boolean canGlide() { // Purpur - if (!this.onGround() && !this.isPassenger() && !this.hasEffect(MobEffects.LEVITATION)) { - Iterator iterator = EquipmentSlot.VALUES.iterator(); - -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 8ccc760e84aec8aabbe5b8112b332cee13849b23..fee0ffaaf94c484c9272d3b5743b90bb70e0d08d 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1664,7 +1664,7 @@ public abstract class Player extends LivingEntity { - } - - @Override -- protected boolean canGlide() { -+ public boolean canGlide() { // Purpur - return !this.abilities.flying && super.canGlide(); - } - -diff --git a/src/main/java/net/minecraft/world/item/FireworkRocketItem.java b/src/main/java/net/minecraft/world/item/FireworkRocketItem.java -index 29a048a9b09166838616ac7ba1d31625d56b0bca..184e6d9bf393188fc1f1c7acd545b4ac6d31f6a4 100644 ---- a/src/main/java/net/minecraft/world/item/FireworkRocketItem.java -+++ b/src/main/java/net/minecraft/world/item/FireworkRocketItem.java -@@ -66,6 +66,18 @@ public class FireworkRocketItem extends Item implements ProjectileItem { - 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) delayed.projectile().getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand)); - if (event.callEvent() && delayed.attemptSpawn()) { - user.awardStat(Stats.ITEM_USED.get(this)); // Moved up from below -+ -+ // Purpur start -+ if (world.purpurConfig.elytraDamagePerFireworkBoost > 0) { -+ List list = net.minecraft.world.entity.EquipmentSlot.VALUES.stream().filter((enumitemslot) -> net.minecraft.world.entity.LivingEntity.canGlideUsing(user.getItemBySlot(enumitemslot), enumitemslot)).toList(); -+ net.minecraft.world.entity.EquipmentSlot enumitemslot = net.minecraft.Util.getRandom(list, user.random); -+ -+ ItemStack glideItem = user.getItemBySlot(enumitemslot); -+ if (user.canGlide()) { -+ glideItem.hurtAndBreak(world.purpurConfig.elytraDamagePerFireworkBoost, user, enumitemslot); -+ } -+ } -+ // Purpur end - if (event.shouldConsume() && !user.hasInfiniteMaterials()) { - itemStack.shrink(1); // Moved up from below - } 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 122859ba52e9719f98111a9c543dab27abac39b5..c616ebbf0f709b8c3a500a23a2190f7023f7757d 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -786,6 +786,12 @@ public final class ItemStack implements DataComponentHolder { - org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent(serverPlayer, this); // Paper - Add EntityDamageItemEvent - } - // CraftBukkit end -+ // Purpur start -+ if (this.has(DataComponents.GLIDER)) { -+ setDamageValue(this.getMaxDamage() - 1); -+ return; -+ } -+ // Purpur end - - this.shrink(1); - breakCallback.accept(item); -diff --git a/src/main/java/net/minecraft/world/item/TridentItem.java b/src/main/java/net/minecraft/world/item/TridentItem.java -index 2b2af4b7cc2c8be8c3aed30885be26816c021bdc..aff4f33be216f62d6c6e139dcd7fd82efdbd267c 100644 ---- a/src/main/java/net/minecraft/world/item/TridentItem.java -+++ b/src/main/java/net/minecraft/world/item/TridentItem.java -@@ -132,6 +132,18 @@ public class TridentItem extends Item implements ProjectileItem { - f4 *= f / f6; - f5 *= f / f6; - org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerRiptideEvent(entityhuman, stack, f3, f4, f5); // CraftBukkit -+ -+ // Purpur start -+ List list = EquipmentSlot.VALUES.stream().filter((enumitemslot) -> LivingEntity.canGlideUsing(entityhuman.getItemBySlot(enumitemslot), enumitemslot)).toList(); -+ if (!list.isEmpty()) { -+ EquipmentSlot enumitemslot = net.minecraft.Util.getRandom(list, entityhuman.random); -+ ItemStack glideItem = entityhuman.getItemBySlot(enumitemslot); -+ if (glideItem.has(net.minecraft.core.component.DataComponents.GLIDER) && world.purpurConfig.elytraDamagePerTridentBoost > 0) { -+ glideItem.hurtAndBreak(world.purpurConfig.elytraDamagePerTridentBoost, entityhuman, enumitemslot); -+ } -+ } -+ // Purpur end -+ - entityhuman.push((double) f3, (double) f4, (double) f5); - entityhuman.startAutoSpinAttack(20, 8.0F, stack); - if (entityhuman.onGround()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 6b7a6a7889353f9557c6e3bfa8ff8caf66416c1b..bc355a6b03877ca5e74688df13a089b487cde86c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -109,6 +109,17 @@ public class PurpurWorldConfig { - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); - } - -+ public int elytraDamagePerSecond = 1; -+ public double elytraDamageMultiplyBySpeed = 0; -+ 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); -+ 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/0059-Item-entity-immunities.patch b/patches/server/0059-Item-entity-immunities.patch deleted file mode 100644 index 27c16c58b6..0000000000 --- a/patches/server/0059-Item-entity-immunities.patch +++ /dev/null @@ -1,172 +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 103e2c414780be66324bcb9cd4ea539bbdfe12ad..dcf1d9c5456bcc7e71a5fbee640998cf41f5d94d 100644 ---- a/src/main/java/net/minecraft/server/level/ServerEntity.java -+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -81,7 +81,7 @@ public class ServerEntity { - @Nullable - private List> trackedDataValues; - // CraftBukkit start -- private final Set trackedPlayers; -+ public final Set trackedPlayers; // Purpur - private -> public - Item entity immunities - - 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 0f086af57a5ff08c264dcbf89a8c3931ec73a609..09403344ad63c538a2a221c6b726ebab1aab5642 100644 ---- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java -@@ -64,6 +64,12 @@ public class ItemEntity extends Entity implements TraceableEntity { - 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 world) { - super(type, world); -@@ -384,7 +390,16 @@ public class ItemEntity extends Entity implements TraceableEntity { - - @Override - public final boolean hurtServer(ServerLevel world, DamageSource source, float amount) { -- if (this.isInvulnerableToBase(source)) { -+ // Purpur start - Item entity immunities -+ if ( -+ (immuneToCactus && source.is(net.minecraft.world.damagesource.DamageTypes.CACTUS)) || -+ (immuneToFire && (source.is(net.minecraft.tags.DamageTypeTags.IS_FIRE) || source.is(net.minecraft.world.damagesource.DamageTypes.ON_FIRE) || source.is(net.minecraft.world.damagesource.DamageTypes.IN_FIRE))) || -+ (immuneToLightning && source.is(net.minecraft.world.damagesource.DamageTypes.LIGHTNING_BOLT)) || -+ (immuneToExplosion && source.is(net.minecraft.tags.DamageTypeTags.IS_EXPLOSION)) -+ ) { -+ return false; -+ } else if (this.isInvulnerableToBase(source)) { -+ // Purpur end - Item entity immunities - return false; - } else if (!world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && source.getEntity() instanceof Mob) { - return false; -@@ -595,6 +610,12 @@ public class ItemEntity extends Entity implements TraceableEntity { - public void setItem(ItemStack stack) { - 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 - 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/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -index 30d62ee4d5cd2ddacb8783b5bbbf475d592b3e02..74f5b702b9602e4c8acbad4fb09c641e2c7844b2 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -@@ -151,4 +151,46 @@ public class CraftItem extends CraftEntity implements Item { - public String toString() { - return "CraftItem"; - } -+ -+ // 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; -+ } -+ -+ @Override -+ public void setImmuneToFire(boolean immuneToFire) { -+ item.immuneToFire = immuneToFire; -+ } -+ -+ @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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index bc355a6b03877ca5e74688df13a089b487cde86c..a750c73519b43f28b0fea69e6056e2752d2eed35 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -120,6 +120,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.getValue(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.parse(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/0060-Add-ping-command.patch b/patches/server/0060-Add-ping-command.patch deleted file mode 100644 index 1f18ba125b..0000000000 --- a/patches/server/0060-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 832930024b0d642496efe17f7e2f8e02413275f0..d4ca48d52c32c5dfb290936fdfc7268f0485aeaf 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -255,6 +255,7 @@ public class Commands { - StopCommand.register(this.dispatcher); - TransferCommand.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 46d06b6887eac6b2e7b3fd9834fdd9284a773f39..33bece7921477677370829c8d5243796bb52deb1 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 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); -@@ -189,6 +190,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 = io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); -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..f202b98a194604e39798fdb8e417c6d2835f71c8 ---- /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.connection.latency()); -+ sender.sendSuccess(output, false); -+ } -+ return targets.size(); -+ } -+} diff --git a/patches/server/0061-Add-demo-command.patch b/patches/server/0061-Add-demo-command.patch deleted file mode 100644 index a6d8ca58b3..0000000000 --- a/patches/server/0061-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 d4ca48d52c32c5dfb290936fdfc7268f0485aeaf..e0b5a1d017cbafdde2c38745a045452c76614b66 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -255,6 +255,7 @@ public class Commands { - StopCommand.register(this.dispatcher); - TransferCommand.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 33bece7921477677370829c8d5243796bb52deb1..790dbbc02a89eeb715cdda339da01379ca704afa 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 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); -@@ -190,6 +191,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/0062-Add-credits-command.patch b/patches/server/0062-Add-credits-command.patch deleted file mode 100644 index 626f386c22..0000000000 --- a/patches/server/0062-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 e0b5a1d017cbafdde2c38745a045452c76614b66..719fbce359f8c2c52ee4e9da3dfe9566f58c0346 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -255,6 +255,7 @@ public class Commands { - StopCommand.register(this.dispatcher); - TransferCommand.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 790dbbc02a89eeb715cdda339da01379ca704afa..df61db286f8b098a16c59a64714554d6d4c60655 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 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() { -@@ -191,6 +192,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/0063-Configurable-jockey-options.patch b/patches/server/0063-Configurable-jockey-options.patch deleted file mode 100644 index d0e6b2ec29..0000000000 --- a/patches/server/0063-Configurable-jockey-options.patch +++ /dev/null @@ -1,276 +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 6675ccfe80cd3c4369ca3f5aec53d0cfa294432b..d471fe99acaf25cb06e36be8a68ab64cfedb4a09 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -104,6 +104,22 @@ public class Drowned extends Zombie implements RangedAttackMob { - this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level().purpurConfig.drownedSpawnReinforcements); - } - // 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/src/main/java/net/minecraft/world/entity/monster/Husk.java b/src/main/java/net/minecraft/world/entity/monster/Husk.java -index 5ad22d4041afcbd50bf5a6ebfbdcb232eb6d3fbe..cd155b47369318f58ec55289a345f1bf28e2af14 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Husk.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java -@@ -51,6 +51,22 @@ 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); - } - // 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 - public static boolean checkHuskSpawnRules(EntityType type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) { - return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (EntitySpawnReason.isSpawner(spawnReason) || 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 5763d259162750297e08acc51551489150dbc593..2ce812ff9cb702a1fc1784f35efb643a2f9036c2 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -134,6 +134,19 @@ public class Zombie extends Monster { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zombieScale); - } - // 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 -@@ -554,19 +567,20 @@ public class Zombie extends Monster { - } - - if (object instanceof Zombie.ZombieGroupData entityzombie_groupdatazombie) { -- 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(), EntitySpawnReason.JOCKEY); - - if (entitychicken1 != null) { -@@ -576,6 +590,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 35d8cef3c84bfd1bbf8afe2885b4f303a4985cdd..36a074627a95886d38bd5a262dddcebfe32d1ba6 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -113,6 +113,22 @@ 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); - } - // 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/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index ca74450f13198fd7bf0190b4dc1df4288df5729d..953df397ea261d417ada15db8d6ffc539ad07c03 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -@@ -87,6 +87,22 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zombifiedPiglinScale); - } - // 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 - 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 a750c73519b43f28b0fea69e6056e2752d2eed35..bb8aa3d4e33e2d8ed4bf3fd668577ea901410783 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -649,6 +649,9 @@ public class PurpurWorldConfig { - 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; - private void drownedSettings() { - drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable); - drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater); -@@ -661,6 +664,9 @@ public class PurpurWorldConfig { - 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); - } - - public boolean elderGuardianRidable = false; -@@ -932,6 +938,9 @@ public class PurpurWorldConfig { - 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; - private void huskSettings() { - huskRidable = getBoolean("mobs.husk.ridable", huskRidable); - huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater); -@@ -944,6 +953,9 @@ public class PurpurWorldConfig { - 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); - } - - public boolean illusionerRidable = false; -@@ -1841,6 +1853,9 @@ public class PurpurWorldConfig { - 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; - private void zombieSettings() { - zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable); - zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater); -@@ -1853,6 +1868,9 @@ public class PurpurWorldConfig { - 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); - } - - public boolean zombieHorseRidable = false; -@@ -1890,6 +1908,9 @@ public class PurpurWorldConfig { - 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; - private void zombieVillagerSettings() { - zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable); - zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater); -@@ -1902,6 +1923,9 @@ public class PurpurWorldConfig { - 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); - } - - public boolean zombifiedPiglinRidable = false; -@@ -1910,6 +1934,9 @@ public class PurpurWorldConfig { - 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; - private void zombifiedPiglinSettings() { - zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable); - zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater); -@@ -1922,5 +1949,8 @@ public class PurpurWorldConfig { - 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); - } - } diff --git a/patches/server/0065-Add-phantom-spawning-options.patch b/patches/server/0065-Add-phantom-spawning-options.patch deleted file mode 100644 index f5e66e1cb1..0000000000 --- a/patches/server/0065-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 021221da5d0315f6e371380a705ac6b3f6ac18d3..27eb9a365006884c85603dc6d9dd8eee009c98b3 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java -@@ -48,7 +48,7 @@ public class PhantomSpawner implements CustomSpawner { - int spawnAttemptMaxSeconds = world.paperConfig().entities.behavior.phantomsSpawnAttemptMaxSeconds; - this.nextTick += (spawnAttemptMinSeconds + randomsource.nextInt(spawnAttemptMaxSeconds - spawnAttemptMinSeconds + 1)) * 20; - // Paper end - Ability to control player's insomnia and phantoms -- if (world.getSkyDarken() < 5 && world.dimensionType().hasSkyLight()) { -+ if (world.getSkyDarken() < world.purpurConfig.phantomSpawnMinSkyDarkness && world.dimensionType().hasSkyLight()) { // Purpur - return 0; - } else { - int i = 0; -@@ -60,10 +60,10 @@ public class PhantomSpawner implements CustomSpawner { - if (!entityplayer.isSpectator() && (!world.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !entityplayer.isCreative())) { // Paper - Add phantom creative and insomniac controls - 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; -@@ -75,7 +75,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 - PhantomPreSpawnEvent -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 429c0bd678cb726b761e32d4b22b33c630f28097..8aebaf667907aa61355ae3a0846b4ee67f204382 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1166,6 +1166,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); -@@ -1190,6 +1196,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/0066-Implement-bed-explosion-options.patch b/patches/server/0066-Implement-bed-explosion-options.patch deleted file mode 100644 index 2f825f86c9..0000000000 --- a/patches/server/0066-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 be700995c3e7f63e0471712c3cd6fd83db583491..db79ef24ea01db5f619ab29700d3d391438fb742 100644 ---- a/src/main/java/net/minecraft/world/level/block/BedBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BedBlock.java -@@ -106,7 +106,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock - - Vec3 vec3d = pos.getCenter(); - -- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d), (ExplosionDamageCalculator) null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); -+ if (world.purpurConfig.bedExplode) world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d), (ExplosionDamageCalculator) null, vec3d, (float) world.purpurConfig.bedExplosionPower, world.purpurConfig.bedExplosionFire, world.purpurConfig.bedExplosionEffect); // Purpur - return InteractionResult.SUCCESS_SERVER; - } else if ((Boolean) state.getValue(BedBlock.OCCUPIED)) { - if (!BedBlock.canSetSpawn(world)) return this.explodeBed(state, world, pos); // Paper - check explode first -@@ -159,7 +159,7 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock - - Vec3 vec3d = blockposition.getCenter(); - -- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, blockState), (ExplosionDamageCalculator) null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state -+ if (world.purpurConfig.bedExplode) world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, blockState), (ExplosionDamageCalculator) null, vec3d, (float) world.purpurConfig.bedExplosionPower, world.purpurConfig.bedExplosionFire, world.purpurConfig.bedExplosionEffect); // CraftBukkit - add state // 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 8aebaf667907aa61355ae3a0846b4ee67f204382..6ffef28deee0a1dfb8c59a9c4d2d2427f8b5eacc 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -279,6 +279,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/0067-Implement-respawn-anchor-explosion-options.patch b/patches/server/0067-Implement-respawn-anchor-explosion-options.patch deleted file mode 100644 index 6b619580de..0000000000 --- a/patches/server/0067-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 9117c035d5a6ff114b028fad3380ceb1fc2b9691..2c5e394156dbf76107adb4913a094dfd4a598dd7 100644 ---- a/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java -@@ -149,7 +149,7 @@ public class RespawnAnchorBlock extends Block { - }; - Vec3 vec3d = explodedPos.getCenter(); - -- world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, blockState), explosiondamagecalculator, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state -+ if (world.purpurConfig.respawnAnchorExplode) world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, blockState), explosiondamagecalculator, vec3d, (float) world.purpurConfig.respawnAnchorExplosionPower, world.purpurConfig.respawnAnchorExplosionFire, world.purpurConfig.respawnAnchorExplosionEffect); // CraftBukkit - add state // 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 6ffef28deee0a1dfb8c59a9c4d2d2427f8b5eacc..6e68accdf6d4067f69cf7b29381ee2eab7a2b20c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -319,6 +319,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 = false; - public boolean turtleEggsBreakFromItems = false; - public boolean turtleEggsBreakFromMinecarts = false; diff --git a/patches/server/0068-Add-allow-water-in-end-world-option.patch b/patches/server/0068-Add-allow-water-in-end-world-option.patch deleted file mode 100644 index 3317f91746..0000000000 --- a/patches/server/0068-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 3bddfb6f7412ab86e0c090d0cbc6cf254b3f891c..b9190acbcb256a3072f0a5f1c4c731f1222b459f 100644 ---- a/src/main/java/net/minecraft/world/item/BucketItem.java -+++ b/src/main/java/net/minecraft/world/item/BucketItem.java -@@ -196,7 +196,7 @@ public class BucketItem extends Item implements DispensibleContainerItem { - // CraftBukkit end - if (!flag2) { - 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 - Add allow water in end world option - int i = blockposition.getX(); - int j = blockposition.getY(); - int k = blockposition.getZ(); -@@ -204,7 +204,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).sendParticlesSource(null, ParticleTypes.LARGE_SMOKE, false, true, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 1, 0.0D, 0.0D, 0.0D, 0.0D); // Purpur - Add allow water in end world option - } - - 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 f3c5e076558cd8d7b9d9b3212872f8675670b2dd..13c99a770e8c466cdba5f397cf8156aed42b55ba 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -2047,4 +2047,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - return this.id; - } - } -+ -+ // 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/src/main/java/net/minecraft/world/level/block/IceBlock.java b/src/main/java/net/minecraft/world/level/block/IceBlock.java -index a94762e65853ccad38cf90b0049ca256106c0c9f..aa93b5041b65dbcdc5bbea65567eaf5d8c433a0d 100644 ---- a/src/main/java/net/minecraft/world/level/block/IceBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/IceBlock.java -@@ -42,7 +42,7 @@ public class IceBlock extends HalfTransparentBlock { - public void afterDestroy(Level world, BlockPos pos, ItemStack tool) { - // Paper end - Improve Block#breakNaturally API - if (!EnchantmentHelper.hasTag(tool, EnchantmentTags.PREVENTS_ICE_MELTING)) { -- if (world.dimensionType().ultraWarm()) { -+ if (world.isNether() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) { // Purpur - Add allow water in end world option - world.removeBlock(pos, false); - return; - } -@@ -70,7 +70,7 @@ public class IceBlock extends HalfTransparentBlock { - return; - } - // CraftBukkit end -- if (world.dimensionType().ultraWarm()) { -+ if (world.isNether() || (world.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) { // Purpur - Add allow water in end world option - 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 df61db286f8b098a16c59a64714554d6d4c60655..5aa172d86f3d36a239147438e8d71c73ec2a3cf0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -247,6 +247,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/0069-Allow-color-codes-in-books.patch b/patches/server/0069-Allow-color-codes-in-books.patch deleted file mode 100644 index 4f767c7b00..0000000000 --- a/patches/server/0069-Allow-color-codes-in-books.patch +++ /dev/null @@ -1,89 +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 830e0705f3a3d45fcf0eab6b8eea403c3023f3f9..0654c2753a44b4cda55fd92513fabc45ff4ab6c9 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1302,10 +1302,14 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - Objects.requireNonNull(list); - optional.ifPresent(list::add); - list.addAll(packet.pages()); -+ // Purpur start -+ boolean hasEditPerm = getCraftPlayer().hasPermission("purpur.book.color.edit"); -+ boolean hasSignPerm = hasEditPerm || getCraftPlayer().hasPermission("purpur.book.color.sign"); -+ // Purpur end - Consumer> consumer = optional.isPresent() ? (list1) -> { -- this.signBook((FilteredText) list1.get(0), list1.subList(1, list1.size()), i); -+ this.signBook((FilteredText) list1.get(0), list1.subList(1, list1.size()), i, hasSignPerm); // Purpur - } : (list1) -> { -- this.updateBookContents(list1, i); -+ this.updateBookContents(list1, i, hasEditPerm); // Purpur - }; - - this.filterTextPacket((List) list).thenAcceptAsync(consumer, this.server); -@@ -1313,13 +1317,18 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - } - - private void updateBookContents(List pages, int slotId) { -+ // Purpur start -+ updateBookContents(pages, slotId, false); -+ } -+ private void updateBookContents(List pages, int slotId, boolean hasPerm) { -+ // Purpur end - // CraftBukkit start - ItemStack handItem = this.player.getInventory().getItem(slotId); - ItemStack itemstack = handItem.copy(); - // CraftBukkit end - - if (itemstack.has(DataComponents.WRITABLE_BOOK_CONTENT)) { -- List> list1 = pages.stream().map(this::filterableFromOutgoing).toList(); -+ List> list1 = pages.stream().map(filteredText -> filterableFromOutgoing(filteredText).map(s -> color(s, hasPerm))).toList(); // Purpur - - itemstack.set(DataComponents.WRITABLE_BOOK_CONTENT, new WritableBookContent(list1)); - this.player.getInventory().setItem(slotId, CraftEventFactory.handleEditBookEvent(this.player, slotId, handItem, itemstack)); // CraftBukkit // Paper - Don't ignore result (see other callsite for handleEditBookEvent) -@@ -1327,6 +1336,11 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - } - - private void signBook(FilteredText title, List pages, int slotId) { -+ // Purpur start -+ signBook(title, pages, slotId, false); -+ } -+ private void signBook(FilteredText title, List pages, int slotId, boolean hasPerm) { -+ // Purpur end - ItemStack itemstack = this.player.getInventory().getItem(slotId); - - if (itemstack.has(DataComponents.WRITABLE_BOOK_CONTENT)) { -@@ -1334,10 +1348,10 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - - itemstack1.remove(DataComponents.WRITABLE_BOOK_CONTENT); - List> list1 = (List>) (List) pages.stream().map((filteredtext1) -> { // CraftBukkit - decompile error -- return this.filterableFromOutgoing(filteredtext1).map(Component::literal); -+ return this.filterableFromOutgoing(filteredtext1).map(s -> hexColor(s, hasPerm)); // Purpur - }).toList(); - -- itemstack1.set(DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(this.filterableFromOutgoing(title), this.player.getName().getString(), 0, list1, true)); -+ itemstack1.set(DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(this.filterableFromOutgoing(title).map(s -> color(s, hasPerm)), this.player.getName().getString(), 0, list1, true)); // Purpur - CraftEventFactory.handleEditBookEvent(this.player, slotId, itemstack, itemstack1); // CraftBukkit - this.player.getInventory().setItem(slotId, itemstack); // CraftBukkit - event factory updates the hand book - } -@@ -1347,6 +1361,16 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - return this.player.isTextFilteringEnabled() ? Filterable.passThrough(message.filteredOrEmpty()) : Filterable.from(message); - } - -+ // Purpur start -+ 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 -+ - @Override - public void handleEntityTagQuery(ServerboundEntityTagQueryPacket packet) { - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); diff --git a/patches/server/0070-Entity-lifespan.patch b/patches/server/0070-Entity-lifespan.patch deleted file mode 100644 index 41314b8680..0000000000 --- a/patches/server/0070-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 0654c2753a44b4cda55fd92513fabc45ff4ab6c9..6b460227a12ba275c02243a1fbafb87e3845cb14 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 extends ServerCommonPacketListenerImpl - AABB axisalignedbb = entity.getBoundingBox(); - - if (this.player.canInteractWithEntity(axisalignedbb, io.papermc.paper.configuration.GlobalConfiguration.get().misc.clientInteractionLeniencyDistance.or(3.0D))) { // Paper - configurable lenience value for interact range -+ 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 261288f51ed47b0eac80cc965c76683f3e13687f..1f253d5ace885423aa25ebdd986e1909a15ba018 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -144,6 +144,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - private BlockPos restrictCenter; - private float restrictRadius; - -+ public int ticksSinceLastInteraction; // Purpur - public boolean aware = true; // CraftBukkit - - protected Mob(EntityType type, Level world) { -@@ -330,6 +331,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - entityliving = null; - } - } -+ if (entityliving instanceof net.minecraft.server.level.ServerPlayer) this.ticksSinceLastInteraction = 0; // Purpur - this.target = entityliving; - return true; - // CraftBukkit end -@@ -374,8 +376,28 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - } - - gameprofilerfiller.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(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD); -+ } -+ } -+ // Purpur end -+ - @Override - protected void playHurtSound(DamageSource damageSource) { - this.resetAmbientSoundTime(); -@@ -544,6 +566,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - } - - nbt.putBoolean("Bukkit.Aware", this.aware); // CraftBukkit -+ nbt.putInt("Purpur.ticksSinceLastInteraction", this.ticksSinceLastInteraction); // Purpur - } - - @Override -@@ -634,6 +657,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - this.aware = nbt.getBoolean("Bukkit.Aware"); - } - // CraftBukkit end -+ // Purpur start -+ if (nbt.contains("Purpur.ticksSinceLastInteraction")) { -+ this.ticksSinceLastInteraction = nbt.getInt("Purpur.ticksSinceLastInteraction"); -+ } -+ // Purpur end - } - - @Override -@@ -1738,6 +1766,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - this.playAttackSound(); - } - -+ if (target instanceof net.minecraft.server.level.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 6e68accdf6d4067f69cf7b29381ee2eab7a2b20c..bfd03c3cdc2b80415730aa1a18fad5afe838df25 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -120,6 +120,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/0071-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch b/patches/server/0071-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch deleted file mode 100644 index 3bb64b5159..0000000000 --- a/patches/server/0071-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 3ea226168519e07b10d015f4c9b2b369569daf11..5d4b0f8b621f62604f098da7bf0822d25b469547 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -3390,4 +3390,26 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - 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.teleport(to); -+ } else { -+ this.server.getPlayerList().respawn(this, true, RemovalReason.KILLED, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason.DEATH, to); -+ } -+ } -+ // 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 d711bef21fba8e32ae99a0a52992e609e4841734..c073786171306ba023f31d2f9ede037707253fa2 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -477,6 +477,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - if (d1 < 0.0D) { - d0 = this.level().getWorldBorder().getDamagePerBlock(); - if (d0 > 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.hurtServer(worldserver1, this.damageSources().outOfBorder(), (float) Math.max(1, Mth.floor(-d1 * d0))); - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index bfd03c3cdc2b80415730aa1a18fad5afe838df25..b5d714f137257810ced4b4457bd5bb01123d3452 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -237,6 +237,7 @@ public class PurpurWorldConfig { - public boolean idleTimeoutTargetPlayer = true; - 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); -@@ -250,6 +251,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0072-Squid-EAR-immunity.patch b/patches/server/0072-Squid-EAR-immunity.patch deleted file mode 100644 index 0c7beebbe9..0000000000 --- a/patches/server/0072-Squid-EAR-immunity.patch +++ /dev/null @@ -1,38 +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 b5d714f137257810ced4b4457bd5bb01123d3452..0d4eaa2933d2b6501eeb64a0d95b2d4d67fb5523 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1586,6 +1586,7 @@ public class PurpurWorldConfig { - public boolean squidControllable = true; - public double squidMaxHealth = 10.0D; - public double squidScale = 1.0D; -+ public boolean squidImmuneToEAR = true; - private void squidSettings() { - squidRidable = getBoolean("mobs.squid.ridable", squidRidable); - squidControllable = getBoolean("mobs.squid.controllable", squidControllable); -@@ -1596,6 +1597,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean spiderRidable = false; -diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index 24d7eca3f0b06602a1026eda3432f0a4255d8b01..9d8d0a99f0b2a8e3452c4c86e6c53c66d58c5b3d 100644 ---- a/src/main/java/org/spigotmc/ActivationRange.java -+++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -378,6 +378,7 @@ public class ActivationRange - */ - public static boolean checkIfActive(Entity entity) - { -+ if (entity.level().purpurConfig.squidImmuneToEAR && entity instanceof net.minecraft.world.entity.animal.Squid) return true; // Purpur - // Never safe to skip fireworks or item gravity - if (entity instanceof FireworkRocketEntity || (entity instanceof ItemEntity && (entity.tickCount + entity.getId()) % 4 == 0)) { // Paper - Needed for item gravity, see ItemEntity tick - return true; diff --git a/patches/server/0073-Phantoms-burn-in-light.patch b/patches/server/0073-Phantoms-burn-in-light.patch deleted file mode 100644 index 0297106011..0000000000 --- a/patches/server/0073-Phantoms-burn-in-light.patch +++ /dev/null @@ -1,76 +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 093c2601ff561c47775f9f99cb5990e6c1b05d8c..7f62544ed66850c9545d8db9897236abd52cbd56 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -50,6 +50,7 @@ public class Phantom extends FlyingMob implements Enemy { - public BlockPos anchorPoint; - Phantom.AttackPhase attackPhase; - Vec3 crystalPosition; // Purpur - Phantoms attracted to crystals and crystals shoot phantoms -+ 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 world) { - super(type, world); -@@ -243,7 +244,11 @@ public class Phantom extends FlyingMob implements Enemy { - - @Override - public void aiStep() { -- if (this.isAlive() && this.shouldBurnInDay && this.isSunBurnTick()) { // Paper - shouldBurnInDay API -+ // Purpur start - Phantoms burn in light -+ boolean burnFromDaylight = this.shouldBurnInDay && this.isSunBurnTick() && 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 - shouldBurnInDay API -+ // Purpur end - Phantoms burn in light - if (getRider() == null || !this.isControllable()) // Purpur - Ridables - this.igniteForSeconds(8.0F); - } -@@ -642,6 +647,12 @@ public class Phantom extends FlyingMob implements Enemy { - return false; - } else if (!entityliving.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(entityliving.getItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND)) || TORCH.test(entityliving.getItemInHand(net.minecraft.world.InteractionHand.OFF_HAND)))) { -+ return false; -+ // Purpur end - Phantoms burn in light - } else { - if (entityliving instanceof Player) { - Player entityhuman = (Player) entityliving; -@@ -788,6 +799,7 @@ public class Phantom extends FlyingMob implements Enemy { - ServerLevel worldserver = getServerLevel(Phantom.this.level()); - List list = worldserver.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 - Phantoms burn in light - 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 0d4eaa2933d2b6501eeb64a0d95b2d4d67fb5523..4fc86678e3c6459d88a0f7114dd858c6850290df 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1221,6 +1221,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); -@@ -1251,6 +1254,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/0074-Configurable-villager-breeding.patch b/patches/server/0074-Configurable-villager-breeding.patch deleted file mode 100644 index 17ee6caf71..0000000000 --- a/patches/server/0074-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 f89c82db6333dc64ac57e225f5522943eb959f46..ca1996ab3b998607c193ba427163a6a144990c97 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -768,7 +768,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 4fc86678e3c6459d88a0f7114dd858c6850290df..83af43e5f1d3e80170104771b2d38526d1ab3e3a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1759,6 +1759,7 @@ public class PurpurWorldConfig { - public boolean villagerFollowEmeraldBlock = false; - public double villagerTemptRange = 10.0D; - 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); -@@ -1773,6 +1774,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean vindicatorRidable = false; diff --git a/patches/server/0075-Redstone-deactivates-spawners.patch b/patches/server/0075-Redstone-deactivates-spawners.patch deleted file mode 100644 index 451dfbe434..0000000000 --- a/patches/server/0075-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 7de66aa435dd36899b80f4ecc64480680e474d94..79a8e5dd1d189c4eaf93999925ea0790eb6ce368 100644 ---- a/src/main/java/net/minecraft/world/level/BaseSpawner.java -+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java -@@ -59,6 +59,7 @@ public abstract class BaseSpawner { - } - - public boolean isNearPlayer(Level world, BlockPos pos) { -+ if (world.purpurConfig.spawnerDeactivateByRedstone && world.hasNeighborSignal(pos)) return false; // Purpur - Redstone deactivates spawners - 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 83af43e5f1d3e80170104771b2d38526d1ab3e3a..be44e9597b595f8a4b63a9a198f63e7db411b6ce 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -347,6 +347,11 @@ public class PurpurWorldConfig { - } - } - -+ public boolean spawnerDeactivateByRedstone = false; -+ private void spawnerSettings() { -+ spawnerDeactivateByRedstone = getBoolean("blocks.spawner.deactivate-by-redstone", spawnerDeactivateByRedstone); -+ } -+ - public boolean turtleEggsBreakFromExpOrbs = false; - public boolean turtleEggsBreakFromItems = false; - public boolean turtleEggsBreakFromMinecarts = false; diff --git a/patches/server/0076-Totems-work-in-inventory.patch b/patches/server/0076-Totems-work-in-inventory.patch deleted file mode 100644 index dfd9bdefb0..0000000000 --- a/patches/server/0076-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 c073786171306ba023f31d2f9ede037707253fa2..dfbb47c200dab7d99cde867c12420c67964867c2 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1718,6 +1718,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 be44e9597b595f8a4b63a9a198f63e7db411b6ce..35ce3c0385f72feea1a0741ee055994f5cf0190a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -238,6 +238,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); -@@ -252,6 +253,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/0077-Add-vindicator-johnny-spawn-chance.patch b/patches/server/0077-Add-vindicator-johnny-spawn-chance.patch deleted file mode 100644 index 197861c4bd..0000000000 --- a/patches/server/0077-Add-vindicator-johnny-spawn-chance.patch +++ /dev/null @@ -1,42 +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 065bd31afe948c1ffab4da6f2a236cd931e75259..7f414f74c315db67a8e8a3d3636811abe733f62a 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -@@ -158,6 +158,11 @@ public class Vindicator extends AbstractIllager { - RandomSource randomSource = world.getRandom(); - this.populateDefaultEquipmentSlots(randomSource, difficulty); - this.populateDefaultEquipmentEnchantments(world, randomSource, difficulty); -+ // Purpur start -+ 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 35ce3c0385f72feea1a0741ee055994f5cf0190a..a4c20f7b679150425b192a9e4411fa987aed8e1f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1789,6 +1789,7 @@ public class PurpurWorldConfig { - public boolean vindicatorControllable = true; - public double vindicatorMaxHealth = 24.0D; - public double vindicatorScale = 1.0D; -+ public double vindicatorJohnnySpawnChance = 0D; - private void vindicatorSettings() { - vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable); - vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater); -@@ -1800,6 +1801,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean wanderingTraderRidable = false; diff --git a/patches/server/0078-Dispensers-place-anvils-option.patch b/patches/server/0078-Dispensers-place-anvils-option.patch deleted file mode 100644 index 6a5c7d733f..0000000000 --- a/patches/server/0078-Dispensers-place-anvils-option.patch +++ /dev/null @@ -1,48 +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 c9d7ac819ce26f5301df7df56edce59b7ef377e0..cdd73bb358e309844bef576175a9026cb8563e7e 100644 ---- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java -+++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java -@@ -930,5 +930,22 @@ public interface DispenseItemBehavior { - 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 -+ 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 - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a4c20f7b679150425b192a9e4411fa987aed8e1f..efebca2a6dd217d853a27516c29be89421c1df68 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -310,8 +310,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/0079-Allow-anvil-colors.patch b/patches/server/0079-Allow-anvil-colors.patch deleted file mode 100644 index 2e7bac233c..0000000000 --- a/patches/server/0079-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 f8c0a4fd95f341cbf8f6a06dfae408d505b0f018..a7c3338ed6edcd26fce869ec66fdcaa4c32c0e4f 100644 ---- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -@@ -285,6 +285,54 @@ public class AnvilMenu extends ItemCombinerMenu { - if (!this.itemName.equals(itemstack.getHoverName().getString())) { - b0 = 1; - i += b0; -+ // 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.set(DataComponents.CUSTOM_NAME, io.papermc.paper.adventure.PaperAdventure.asVanilla(component)); -+ } -+ else -+ // Purpur end - itemstack1.set(DataComponents.CUSTOM_NAME, Component.literal(this.itemName)); - } - } else if (itemstack.has(DataComponents.CUSTOM_NAME)) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index efebca2a6dd217d853a27516c29be89421c1df68..7a7b8725f7a0b1a193b4e19510c386b46604f006 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -288,6 +288,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/0080-Add-option-to-disable-dolphin-treasure-searching.patch b/patches/server/0080-Add-option-to-disable-dolphin-treasure-searching.patch deleted file mode 100644 index 234dcbef5e..0000000000 --- a/patches/server/0080-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 2ee7de39712d67b593ff287a9ed17c28fa768b3c..5dfaed8ecf9438e8ea11cadd6c5638e0cc25cbb1 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -494,6 +494,7 @@ public class Dolphin extends AgeableWaterCreature { - - @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 7a7b8725f7a0b1a193b4e19510c386b46604f006..109c2d5ba677eee65f44cd93b0fdcee22d04d17b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -669,6 +669,7 @@ public class PurpurWorldConfig { - public float dolphinSpitDamage = 2.0F; - public double dolphinMaxHealth = 10.0D; - public double dolphinScale = 1.0D; -+ public boolean dolphinDisableTreasureSearching = false; - private void dolphinSettings() { - dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable); - dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable); -@@ -682,6 +683,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean donkeyRidableInWater = false; diff --git a/patches/server/0081-Short-enderman-height.patch b/patches/server/0081-Short-enderman-height.patch deleted file mode 100644 index 27430dfb29..0000000000 --- a/patches/server/0081-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 426d47c14d137906ed70fc3082e3d77e7f48c8d7..20a7fe2995db717f394fc3041435a95dbfee2ff7 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityType.java -+++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -388,7 +388,8 @@ public class EntityType implements FeatureElement, EntityTypeT - @Nullable - private Component description; - private final Optional> lootTable; -- private final EntityDimensions dimensions; -+ private EntityDimensions dimensions; // Purpur - remove final - Short enderman height -+ public void setDimensions(EntityDimensions dimensions) { this.dimensions = dimensions; } // Purpur - Short enderman height - private final float spawnDimensionsScale; - private final FeatureFlagSet requiredFeatures; - -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 13eaf1e8b12c3b4cba6b202f2d31e99a8a9bae92..d2959976cf206f6c92ae90f103035e4760b9d955 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -409,6 +409,7 @@ public class EnderMan extends Monster implements NeutralMob { - if (this.isInvulnerableTo(world, source)) { - return false; - } else if (getRider() != null && this.isControllable()) { return super.hurtServer(world, 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 - Short enderman 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 5aa172d86f3d36a239147438e8d71c73ec2a3cf0..6ba2e3ed28b7605329446b84dc305e71505e30ec 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -247,6 +247,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/0082-Stop-squids-floating-on-top-of-water.patch b/patches/server/0082-Stop-squids-floating-on-top-of-water.patch deleted file mode 100644 index 096a49e27a..0000000000 --- a/patches/server/0082-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 eea1053e0ec1446e117d3ac4732deaba191db8f2..2cf83678a2a37ff9082eae1a8b6cbf5be60a065f 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -4775,6 +4775,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - return Mth.lerp(delta, this.yRotO, this.yRot); - } - -+ // Purpur start -+ public AABB getAxisForFluidCheck() { -+ return this.getBoundingBox().deflate(0.001D); -+ } -+ // Purpur end -+ - // Paper start - optimise collisions - public boolean updateFluidHeightAndDoFluidPushing(final TagKey fluid, final double flowScale) { - if (this.touchingUnloadedChunk()) { -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 bb63ad576731df34a90ab9b1b6165731a826ad7b..490fa58d09d58cefb1adef1ba11823ba14fa2855 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -79,6 +79,12 @@ public class Squid extends AgeableWaterCreature { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.squidScale); - } - // Purpur end - Configurable entity base attributes -+ @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 6cf6d4ec7b9e43c7b2b4c0e2fb080964ff588130..e74866e5195a5eeae7666ad7be750edac5947094 100644 ---- a/src/main/java/net/minecraft/world/phys/AABB.java -+++ b/src/main/java/net/minecraft/world/phys/AABB.java -@@ -551,4 +551,10 @@ public class AABB { - public static AABB ofSize(Vec3 center, double dx, double dy, double dz) { - return new AABB(center.x - dx / 2.0, center.y - dy / 2.0, center.z - dz / 2.0, center.x + dx / 2.0, center.y + dy / 2.0, center.z + dz / 2.0); - } -+ -+ // 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 109c2d5ba677eee65f44cd93b0fdcee22d04d17b..b04c1c2534bec8f2eee873b43969843f54644dca 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1611,6 +1611,7 @@ public class PurpurWorldConfig { - public double squidMaxHealth = 10.0D; - public double squidScale = 1.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); -@@ -1622,6 +1623,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean spiderRidable = false; diff --git a/patches/server/0083-Crying-obsidian-valid-for-portal-frames.patch b/patches/server/0083-Crying-obsidian-valid-for-portal-frames.patch deleted file mode 100644 index 3b44dd6ad4..0000000000 --- a/patches/server/0083-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 90056822cd17f3d33d14b3f94b34750ee522a0a9..acdff7b4a00d563739fd301c3633a266875296fa 100644 ---- a/src/main/java/net/minecraft/world/level/portal/PortalShape.java -+++ b/src/main/java/net/minecraft/world/level/portal/PortalShape.java -@@ -35,7 +35,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 6ba2e3ed28b7605329446b84dc305e71505e30ec..c61105bfb22531b728cb4f4af24c68625db5a99f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -215,6 +215,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); -@@ -245,6 +246,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/0084-Entities-can-use-portals.patch b/patches/server/0084-Entities-can-use-portals.patch deleted file mode 100644 index 31378a71e9..0000000000 --- a/patches/server/0084-Entities-can-use-portals.patch +++ /dev/null @@ -1,47 +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 - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 2cf83678a2a37ff9082eae1a8b6cbf5be60a065f..c0e8739f0dd9290550872839eddca4b0090b603b 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3468,7 +3468,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - public void setAsInsidePortal(Portal portal, BlockPos pos) { - if (this.isOnPortalCooldown()) { - 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)) { - if (!this.portalProcess.isInsidePortalThisTick()) { - this.portalProcess.updateEntryPosition(pos.immutable()); -@@ -4191,7 +4191,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - // CraftBukkit end - - public boolean canUsePortal(boolean allowVehicles) { -- return (allowVehicles || !this.isPassenger()) && this.isAlive(); -+ return (allowVehicles || !this.isPassenger()) && this.isAlive() && (this.level.purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer); // Purpur - Entities can use portals - } - - public boolean canTeleport(Level from, Level to) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b04c1c2534bec8f2eee873b43969843f54644dca..a28548332baf610521a308a560a858b3fc737b60 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -101,12 +101,14 @@ public class PurpurWorldConfig { - public boolean disableDropsOnCrammingDeath = false; - public boolean milkCuresBadOmen = true; - public double tridentLoyaltyVoidReturnHeight = 0.0D; -+ public boolean entitiesCanUsePortals = true; - 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); - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); -+ entitiesCanUsePortals = getBoolean("gameplay-mechanics.entities-can-use-portals", entitiesCanUsePortals); - } - - public int elytraDamagePerSecond = 1; diff --git a/patches/server/0085-Customizable-wither-health-and-healing.patch b/patches/server/0085-Customizable-wither-health-and-healing.patch deleted file mode 100644 index a6988337b9..0000000000 --- a/patches/server/0085-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 57cf253fc69c087cfa0270225166f926ce6829f7..098ef0d6879e88009193c6dcedf1aa285980cbc6 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 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 a28548332baf610521a308a560a858b3fc737b60..e323914f8694043e7b08a2518169695f582bc1a8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1876,6 +1876,8 @@ public class PurpurWorldConfig { - public double witherMaxY = 320D; - public double witherMaxHealth = 300.0D; - public double witherScale = 1.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); -@@ -1892,6 +1894,8 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean witherSkeletonRidable = false; diff --git a/patches/server/0086-Allow-toggling-special-MobSpawners-per-world.patch b/patches/server/0086-Allow-toggling-special-MobSpawners-per-world.patch deleted file mode 100644 index 3c1a883fed..0000000000 --- a/patches/server/0086-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 7ab130afddb0893016fb558ed624198316ca191d..3442ace00904b73f4384760d42c9385697132bc9 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -606,7 +606,24 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - // CraftBukkit end - 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 a728dcbf956f108f01c966c7531449a506a14a87..4c1378132201c1e5d1bc01f8c0cbba91629bcffa 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTraderSpawner.java -@@ -160,7 +160,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 (spawnplacementtype.isSpawnPositionOk(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 e323914f8694043e7b08a2518169695f582bc1a8..9072bdd2fdb1c15ea1dbc599cb96fd82750ddcc6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -70,6 +70,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)); -@@ -232,6 +238,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/0087-Raid-cooldown-setting.patch b/patches/server/0087-Raid-cooldown-setting.patch deleted file mode 100644 index b1805bb7b9..0000000000 --- a/patches/server/0087-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 439d61d8689fabe940006b9b317a6810175dccfb..6b30941a84054efb5fcccb5d9e6c80d713a23889 100644 ---- a/src/main/java/net/minecraft/world/entity/raid/Raids.java -+++ b/src/main/java/net/minecraft/world/entity/raid/Raids.java -@@ -26,6 +26,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; -@@ -51,6 +52,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()) { -@@ -122,11 +134,13 @@ public class Raids extends SavedData { - */ - - 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 -+ 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(net.minecraft.world.effect.MobEffects.RAID_OMEN); - return null; - } -+ if (level.purpurConfig.raidCooldownSeconds != 0) playerCooldowns.put(player.getUUID(), level.purpurConfig.raidCooldownSeconds); // Purpur - - if (!raid.isStarted() && !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 9072bdd2fdb1c15ea1dbc599cb96fd82750ddcc6..0e7473973fe06f51e28bb8651856ed52a9a51d92 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -108,6 +108,7 @@ public class PurpurWorldConfig { - public boolean milkCuresBadOmen = true; - public double tridentLoyaltyVoidReturnHeight = 0.0D; - public boolean entitiesCanUsePortals = true; -+ 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); -@@ -115,6 +116,7 @@ public class PurpurWorldConfig { - 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); - } - - public int elytraDamagePerSecond = 1; diff --git a/patches/server/0088-Add-option-to-disable-zombie-aggressiveness-towards-.patch b/patches/server/0088-Add-option-to-disable-zombie-aggressiveness-towards-.patch deleted file mode 100644 index 3ebd24c6fe..0000000000 --- a/patches/server/0088-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 6bdc683b5ade408ee27f1d6636b4d60c8c89cb7c..11d91f58208c1e816620f5b97c5fdfc6ce37f6c3 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 d471fe99acaf25cb06e36be8a68ab64cfedb4a09..3f02cb4924817ed132d2b17b97a67f7d7bf57cb8 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -130,7 +130,19 @@ public class Drowned extends Zombie implements RangedAttackMob { - 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, (target, world) -> this.okTarget(target))); -- if (this.level().spigotConfig.zombieAggressiveTowardsVillager) this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)); // Paper - Check drowned for villager aggression config -+ // Purpur start -+ 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 - 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 2ce812ff9cb702a1fc1784f35efb643a2f9036c2..473ab54ac90f00bf5baf6b53190fa0f4f762700b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -163,7 +163,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 0e7473973fe06f51e28bb8651856ed52a9a51d92..f6771afafe3e9e672059f504982e90449492449c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1984,6 +1984,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); -@@ -1999,6 +2000,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 zombieHorseRidable = false; diff --git a/patches/server/0089-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch b/patches/server/0089-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch deleted file mode 100644 index 08d1f639c9..0000000000 --- a/patches/server/0089-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch +++ /dev/null @@ -1,42 +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 0b0054b3d5d56ba24e1aee0e3ab56ea5b01a82a8..2e3a834643d56543418e9b9beb9d3448bf059d22 100644 ---- a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -+++ b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -@@ -45,6 +45,7 @@ public final class Ingredient implements StackedContents.IngredientInfo itemStacks; // Paper - Improve exact choice recipe ingredients -+ public Predicate predicate; // Purpur - - public boolean isExact() { - return this.itemStacks != null; -@@ -100,6 +101,11 @@ public final class Ingredient implements StackedContents.IngredientInfo 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 - // 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/patches/server/0090-Flying-squids-Oh-my.patch b/patches/server/0090-Flying-squids-Oh-my.patch deleted file mode 100644 index dad94609dc..0000000000 --- a/patches/server/0090-Flying-squids-Oh-my.patch +++ /dev/null @@ -1,93 +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 66a5c485ed2d29d0079ae714c2dd7b01aab11d86..d556642fd07492aa52bfc0a2432ab3d16fe4f866 100644 ---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java -+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java -@@ -44,6 +44,11 @@ public class GlowSquid extends Squid { - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.glowSquidMaxHealth); - } - // Purpur end - Configurable entity base attributes -+ @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 490fa58d09d58cefb1adef1ba11823ba14fa2855..d976fca9b9764f9298bc9aa2e28b6f0feed1939f 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -85,6 +85,15 @@ public class Squid extends AgeableWaterCreature { - 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)); -@@ -163,6 +172,7 @@ public class Squid extends AgeableWaterCreature { - } - - 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; -@@ -375,7 +385,7 @@ public class Squid extends AgeableWaterCreature { - int i = this.squid.getNoActionTime(); - if (i > 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 - float f = this.squid.getRandom().nextFloat() * (float) (Math.PI * 2); - this.squid.movementVector = new Vec3( - (double)(Mth.cos(f) * 0.2F), (double)(-0.1F + this.squid.getRandom().nextFloat() * 0.2F), (double)(Mth.sin(f) * 0.2F) -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f6771afafe3e9e672059f504982e90449492449c..fea279b9fbd7f989bf26f0fc1254b1ecb8ac4342 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -946,11 +946,13 @@ public class PurpurWorldConfig { - public boolean glowSquidControllable = true; - public double glowSquidMaxHealth = 10.0D; - public double glowSquidScale = 1.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); - glowSquidScale = Mth.clamp(getDouble("mobs.glow_squid.attributes.scale", glowSquidScale), 0.0625D, 16.0D); -+ glowSquidsCanFly = getBoolean("mobs.glow_squid.can-fly", glowSquidsCanFly); - } - - public boolean goatRidable = false; -@@ -1637,6 +1639,7 @@ public class PurpurWorldConfig { - public double squidScale = 1.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); -@@ -1649,6 +1652,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean spiderRidable = false; diff --git a/patches/server/0091-Infinity-bow-settings.patch b/patches/server/0091-Infinity-bow-settings.patch deleted file mode 100644 index 727127486b..0000000000 --- a/patches/server/0091-Infinity-bow-settings.patch +++ /dev/null @@ -1,47 +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 bb593209c95c9cf1f9c5d52d52fab4a33ddbabcf..1d4d0799a86b9940b5e3b614c5a188ade5133f7e 100644 ---- a/src/main/java/net/minecraft/world/item/BowItem.java -+++ b/src/main/java/net/minecraft/world/item/BowItem.java -@@ -28,6 +28,11 @@ public class BowItem extends ProjectileWeaponItem { - return false; - } else { - ItemStack itemStack = player.getProjectile(stack); -+ // Purpur start -+ if (world.purpurConfig.infinityWorksWithoutArrows && itemStack.isEmpty() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.INFINITY, stack) > 0) { -+ itemStack = new ItemStack(Items.ARROW); -+ } -+ // Purpur end - if (itemStack.isEmpty()) { - return false; - } else { -@@ -89,7 +94,7 @@ public class BowItem extends ProjectileWeaponItem { - public InteractionResult use(Level world, Player user, InteractionHand hand) { - ItemStack itemStack = user.getItemInHand(hand); - boolean bl = !user.getProjectile(itemStack).isEmpty(); -- if (!user.hasInfiniteMaterials() && !bl) { -+ if (!user.hasInfiniteMaterials() && !bl && !(world.purpurConfig.infinityWorksWithoutArrows && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.INFINITY, itemStack) > 0)) { // Purpur - return InteractionResult.FAIL; - } 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 fea279b9fbd7f989bf26f0fc1254b1ecb8ac4342..0716bd2728be4fcaa4f13485591008ce270ecf82 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -135,6 +135,11 @@ public class PurpurWorldConfig { - entityLifeSpan = getInt("gameplay-mechanics.entity-lifespan", entityLifeSpan); - } - -+ 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<>(); diff --git a/patches/server/0092-Configurable-daylight-cycle.patch b/patches/server/0092-Configurable-daylight-cycle.patch deleted file mode 100644 index 4a3c55c7ab..0000000000 --- a/patches/server/0092-Configurable-daylight-cycle.patch +++ /dev/null @@ -1,94 +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/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 75ef1a4f80449e87affbd9ba62f5f9447ef9db87..d55390d725f8798becb0b7b04485c99611e0e527 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1858,7 +1858,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 a9809c18233d82f910735e59363a49de488defcd..affa084b6bd4a6792d1c55719f88f3fb16627761 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 -@@ -218,6 +218,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)) { -+ net.minecraft.world.level.material.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 - ItemStack itemstack1 = (ItemStack) blockEntity.items.get(0); - boolean flag2 = !itemstack1.isEmpty(); - boolean flag3 = !itemstack.isEmpty(); -@@ -303,6 +318,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 dynamicRegistryManager, @Nullable RecipeHolder recipe, SingleRecipeInput input, NonNullList inventory, int maxCount) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1060208faaa18020c5762a2eb4851f6148b5f215..fda9fcf681927334937d25706c9127fbbecd2ed5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -365,6 +365,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/0094-Arrows-should-not-reset-despawn-counter.patch b/patches/server/0094-Arrows-should-not-reset-despawn-counter.patch deleted file mode 100644 index c215cf5edb..0000000000 --- a/patches/server/0094-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 f4513b1887f823b088dabe425be042b8fb2bde66..d2a8e0fb1e673cec5701fd184cedfd8e49212acd 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -@@ -372,7 +372,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 - } - - public boolean isInGround() { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fda9fcf681927334937d25706c9127fbbecd2ed5..2e807c72b8469210c4acdc591386368589913e61 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -102,6 +102,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/0095-Ability-to-re-add-farmland-mechanics-from-Alpha.patch b/patches/server/0095-Ability-to-re-add-farmland-mechanics-from-Alpha.patch deleted file mode 100644 index 31c06b3c77..0000000000 --- a/patches/server/0095-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 df89b18f0d5d2dad5745ec65bcd9e4a7bf2f1f2b..e744a117d2f25ad24c87263ba2ab7f760b6e594d 100644 ---- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -@@ -126,6 +126,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 2e807c72b8469210c4acdc591386368589913e61..acc45259de6b0178408146b71ebaf4de9d1891bd 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -366,8 +366,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/0096-Add-adjustable-breeding-cooldown-to-config.patch b/patches/server/0096-Add-adjustable-breeding-cooldown-to-config.patch deleted file mode 100644 index ca6cd7904e..0000000000 --- a/patches/server/0096-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 5677dc97ed83652f261100cf391883cfac7d16fe..4637d062b6a4c4d6de2ef54ec3cfdeb4344ff38e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Animal.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Animal.java -@@ -157,7 +157,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 - final ItemStack breedCopy = itemstack.copy(); // Paper - Fix EntityBreedEvent copying - this.usePlayerItem(player, hand, itemstack); - this.setInLove(player, breedCopy); // Paper - Fix EntityBreedEvent copying -@@ -261,12 +261,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 13c99a770e8c466cdba5f397cf8156aed42b55ba..1aa251cfdf941027f529ce02f0c16c815ade296f 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -183,6 +183,49 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - public final Map explosionDensityCache = new 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 -+ 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; - } -@@ -845,6 +888,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).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) worlddatamutable).getLevelName(), env); // Purpur - Purpur config files -+ 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 acc45259de6b0178408146b71ebaf4de9d1891bd..6805a2f2803bb4e859afba5369308dd4d2ac5165 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -114,6 +114,7 @@ public class PurpurWorldConfig { - public double tridentLoyaltyVoidReturnHeight = 0.0D; - public boolean entitiesCanUsePortals = true; - 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); -@@ -122,6 +123,7 @@ public class PurpurWorldConfig { - 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); - } - - 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/0097-Make-entity-breeding-times-configurable.patch b/patches/server/0097-Make-entity-breeding-times-configurable.patch deleted file mode 100644 index e6ba99a759..0000000000 --- a/patches/server/0097-Make-entity-breeding-times-configurable.patch +++ /dev/null @@ -1,1014 +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 0a608418f87b71d5d71706712e1f82da0d7e4d34..03e7ca83e4c28dfaa5b52bcb100bd542db105970 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 -@@ -125,8 +125,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 4637d062b6a4c4d6de2ef54ec3cfdeb4344ff38e..8df1682dca61b0f771a1c08f5bcd797506badf19 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Animal.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Animal.java -@@ -49,6 +49,7 @@ public abstract class Animal extends AgeableMob { - @Nullable - public UUID loveCause; - public ItemStack breedItem; // CraftBukkit - Add breedItem variable -+ public abstract int getPurpurBreedTime(); // Purpur - Make entity breeding times configurable - - protected Animal(EntityType type, Level world) { - super(type, world); -@@ -302,8 +303,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 - Make entity breeding times configurable -+ this.setAge(this.getPurpurBreedTime()); -+ entityanimal.setAge(entityanimal.getPurpurBreedTime()); -+ // Purpur end - Make entity breeding times configurable - 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 8751cc882f2dcbf6dfc10cebab9d9a4f95ebfb10..121a4c58d4052eb0880f540e87beaf12a74eecd7 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -496,6 +496,12 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.beeScale); - } - // 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 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 b4f022093a52c1fe13ad67ad70d57fd0278a9d55..06b68fcf750a637e1f41eecacca2969cb55a8fa8 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,12 @@ public class Cat extends TamableAnimal implements VariantHolder { -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 f6f7afc6a421022738f77e5eece193ad15de78ba..d06fb7d714efc88af13e6357536d4c58a7ec3bb5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -82,6 +82,12 @@ public class Chicken extends Animal { - // Purpur end - Chickens can retaliate - } - // 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/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index 88dd147317e200a11eb6a7e496bfffee9079fd6f..4b27440624deda151f63f7ce4670fd805d24eaba 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -68,6 +68,12 @@ public class Cow extends Animal { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.cowScale); - } - // 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 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 a0adc29bb09a3eaecdaf564fd992b8aefec69629..ce597675e973eb0d7ce580a2b4dfd76e7bb586b1 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -189,6 +189,12 @@ public class Fox extends Animal implements VariantHolder { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.foxScale); - } - // 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); -@@ -977,8 +983,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 - 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(); - 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 96078cf11f13c85a3f019f70fa02668c43dbc559..28b544f4f2e0f30d831b57167fc3ea3ce28e2191 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -87,6 +87,12 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.rabbitScale); - } - // 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/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -index 3fd84abe0889b224c7c8d5af06b384d2fb431eb4..761ff22d66a191ab4c38d872e65afc2ee70f233d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java -@@ -108,6 +108,12 @@ public class Sheep extends Animal implements Shearable { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.sheepScale); - } - // 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/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java -index 2133b8a2615be6502bf9d7b4d4c1bc75929b5022..0a29ce3a783a5bcadfd386927f176457596882ce 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,12 @@ public class Turtle extends Animal { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.turtleScale); - } - // 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 pos) { - this.entityData.set(Turtle.HOME_POS, pos); - } -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 d86bad821e781d698cc2bf0b3179eb4d2ed065b1..539eba7148be12eb05c907ed86b0cea975424874 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -@@ -153,6 +153,12 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder, B - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.axolotlScale); - } - // 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 world) { - return 0.0F; -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 ab0b90c300d0610e423abe7ac9e5b93305a21c5a..d0023e3734bb3c625fa53077f47039dcb82d9606 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 -@@ -94,6 +94,12 @@ 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 - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); -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 b42e1e1749c9f18b3bf842517522e90ba60330ff..e862803b38aceec149da8deddb9c88c46e916ad6 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 -@@ -165,6 +165,12 @@ public class Frog extends Animal implements VariantHolder> { - } - // 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/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index 307d11e5ad6d0b47af36b1469a73ae1caa100232..c9d7be823a040e7de407537d247062821dca643a 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 -@@ -110,6 +110,12 @@ 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(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 8fd709bcd4c7a5a875bdc65fd4dd1420ea618e3a..4cff4fcedc75a99ca71ae6a7c34d6f407665bf1b 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 -@@ -39,6 +39,12 @@ public class Donkey extends AbstractChestedHorse { - return generateSpeed(this.level().purpurConfig.donkeyMovementSpeedMin, this.level().purpurConfig.donkeyMovementSpeedMax); - } - // 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 - 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 13056a0a13eeb3dcc164344b973e6ff656c0793d..cbeb7d1e834a4f0f120248bec619e34ca46f8069 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 -@@ -66,6 +66,12 @@ public class Horse extends AbstractHorse implements VariantHolder { - return generateSpeed(this.level().purpurConfig.horseMovementSpeedMin, this.level().purpurConfig.horseMovementSpeedMax); - } - // 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((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 cbab482bd44c0a0fa82a80f41fdfd8c124c58c43..ea396b5740ae1e7b2cf5356607835467b412d379 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 -@@ -140,6 +140,12 @@ public class Llama extends AbstractChestedHorse implements VariantHolder type, LevelAccessor world, EntitySpawnReason 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 476dcc613f566d88273f195b847e6b4dec777e44..d7bef344f1d18aa6037b3805da07353c54c82142 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 -@@ -100,6 +100,12 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - this.timeInOverworld = timeInOverworld; - } - -+ // Purpur start - Make entity breeding times configurable -+ @Override -+ public int getPurpurBreedTime() { -+ return this.level().purpurConfig.hoglinBreedingTicks; -+ } -+ // Purpur end - Make entity breeding times configurable - @Override - public boolean canBeLeashed() { - return true; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 6805a2f2803bb4e859afba5369308dd4d2ac5165..e363a93bdfcef7408ecdd5618b63c345ab9525b4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -463,23 +463,27 @@ public class PurpurWorldConfig { - 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; - 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); - } - - public boolean batRidable = false; -@@ -522,6 +526,7 @@ public class PurpurWorldConfig { - public double beeMaxY = 320D; - public double beeMaxHealth = 10.0D; - public double beeScale = 1.0D; -+ public int beeBreedingTicks = 6000; - private void beeSettings() { - beeRidable = getBoolean("mobs.bee.ridable", beeRidable); - beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater); -@@ -534,6 +539,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean blazeRidable = false; -@@ -576,6 +582,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); -@@ -584,6 +591,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; -@@ -594,6 +602,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); -@@ -608,6 +617,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; -@@ -634,6 +644,7 @@ public class PurpurWorldConfig { - public double chickenMaxHealth = 4.0D; - public double chickenScale = 1.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); -@@ -646,6 +657,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean codRidable = false; -@@ -670,6 +682,7 @@ public class PurpurWorldConfig { - public double cowMaxHealth = 10.0D; - public double cowScale = 1.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 +695,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean creakingRidable = false; -@@ -750,6 +764,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) { -@@ -765,6 +780,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; -@@ -895,6 +911,7 @@ public class PurpurWorldConfig { - public double foxMaxHealth = 10.0D; - public double foxScale = 1.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); -@@ -907,17 +924,20 @@ public class PurpurWorldConfig { - 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); - } - - 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; -@@ -992,12 +1012,14 @@ public class PurpurWorldConfig { - public boolean goatControllable = true; - public double goatMaxHealth = 10.0D; - public double goatScale = 1.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); - goatScale = Mth.clamp(getDouble("mobs.goat.attributes.scale", goatScale), 0.0625D, 16.0D); -+ goatBreedingTicks = getInt("mobs.goat.breeding-delay-ticks", goatBreedingTicks); - } - - public boolean guardianRidable = false; -@@ -1021,6 +1043,7 @@ public class PurpurWorldConfig { - public boolean hoglinControllable = true; - public double hoglinMaxHealth = 40.0D; - public double hoglinScale = 1.0D; -+ public int hoglinBreedingTicks = 6000; - private void hoglinSettings() { - hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable); - hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater); -@@ -1032,6 +1055,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean horseRidableInWater = false; -@@ -1041,6 +1065,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) { -@@ -1056,6 +1081,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; -@@ -1139,6 +1165,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); -@@ -1156,6 +1183,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; -@@ -1185,6 +1213,7 @@ public class PurpurWorldConfig { - public boolean mooshroomControllable = true; - public double mooshroomMaxHealth = 10.0D; - public double mooshroomScale = 1.0D; -+ public int mooshroomBreedingTicks = 6000; - private void mooshroomSettings() { - mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable); - mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater); -@@ -1196,6 +1225,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean muleRidableInWater = false; -@@ -1205,6 +1235,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) { -@@ -1220,6 +1251,7 @@ 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; -@@ -1227,6 +1259,7 @@ public class PurpurWorldConfig { - public boolean ocelotControllable = true; - public double ocelotMaxHealth = 10.0D; - public double ocelotScale = 1.0D; -+ public int ocelotBreedingTicks = 6000; - private void ocelotSettings() { - ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable); - ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater); -@@ -1238,6 +1271,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean pandaRidable = false; -@@ -1245,6 +1279,7 @@ public class PurpurWorldConfig { - public boolean pandaControllable = true; - public double pandaMaxHealth = 20.0D; - public double pandaScale = 1.0D; -+ public int pandaBreedingTicks = 6000; - private void pandaSettings() { - pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable); - pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater); -@@ -1256,6 +1291,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean parrotRidable = false; -@@ -1342,6 +1378,7 @@ public class PurpurWorldConfig { - public double pigMaxHealth = 10.0D; - public double pigScale = 1.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); -@@ -1354,6 +1391,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean piglinRidable = false; -@@ -1417,6 +1455,7 @@ public class PurpurWorldConfig { - public double polarBearScale = 1.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); -@@ -1431,6 +1470,7 @@ public class PurpurWorldConfig { - polarBearBreedableItemString = getString("mobs.polar_bear.breedable-item", polarBearBreedableItemString); - Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(polarBearBreedableItemString)); - if (item != Items.AIR) polarBearBreedableItem = item; -+ polarBearBreedingTicks = getInt("mobs.polar_bear.breeding-delay-ticks", polarBearBreedingTicks); - } - - public boolean pufferfishRidable = false; -@@ -1456,6 +1496,7 @@ public class PurpurWorldConfig { - public double rabbitScale = 1.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); -@@ -1469,6 +1510,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean ravagerRidable = false; -@@ -1510,6 +1552,7 @@ public class PurpurWorldConfig { - public boolean sheepControllable = true; - public double sheepMaxHealth = 8.0D; - public double sheepScale = 1.0D; -+ public int sheepBreedingTicks = 6000; - private void sheepSettings() { - sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable); - sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater); -@@ -1521,6 +1564,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean shulkerRidable = false; -@@ -1657,12 +1701,14 @@ public class PurpurWorldConfig { - 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; -@@ -1728,6 +1774,7 @@ public class PurpurWorldConfig { - public boolean striderControllable = true; - public double striderMaxHealth = 20.0D; - public double striderScale = 1.0D; -+ public int striderBreedingTicks = 6000; - private void striderSettings() { - striderRidable = getBoolean("mobs.strider.ridable", striderRidable); - striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater); -@@ -1739,6 +1786,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean tadpoleRidable = false; -@@ -1759,6 +1807,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); -@@ -1776,6 +1825,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; -@@ -1799,6 +1849,7 @@ public class PurpurWorldConfig { - public boolean turtleControllable = true; - public double turtleMaxHealth = 30.0D; - public double turtleScale = 1.0D; -+ public int turtleBreedingTicks = 6000; - private void turtleSettings() { - turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable); - turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater); -@@ -1810,6 +1861,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean vexRidable = false; -@@ -1841,6 +1893,7 @@ public class PurpurWorldConfig { - public double villagerTemptRange = 10.0D; - 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); -@@ -1856,6 +1909,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean vindicatorRidable = false; -@@ -1980,6 +2034,7 @@ public class PurpurWorldConfig { - public boolean wolfControllable = true; - public double wolfMaxHealth = 8.0D; - public double wolfScale = 1.0D; -+ public int wolfBreedingTicks = 6000; - private void wolfSettings() { - wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable); - wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater); -@@ -1991,6 +2046,7 @@ public class PurpurWorldConfig { - } - wolfMaxHealth = getDouble("mobs.wolf.attributes.max_health", wolfMaxHealth); - wolfScale = Mth.clamp(getDouble("mobs.wolf.attributes.scale", wolfScale), 0.0625D, 16.0D); -+ wolfBreedingTicks = getInt("mobs.wolf.breeding-delay-ticks", wolfBreedingTicks); - } - - public boolean zoglinRidable = false; diff --git a/patches/server/0098-Apply-display-names-from-item-forms-of-entities-to-e.patch b/patches/server/0098-Apply-display-names-from-item-forms-of-entities-to-e.patch deleted file mode 100644 index c26f250166..0000000000 --- a/patches/server/0098-Apply-display-names-from-item-forms-of-entities-to-e.patch +++ /dev/null @@ -1,159 +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 edb0cd90e28016c44b0aaf5c9ed5d7bdbced5295..12ff824ffa81ea45f76337ec2b6d80b01047b698 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -618,6 +618,7 @@ public class ArmorStand extends LivingEntity { - private org.bukkit.event.entity.EntityDeathEvent brokenByPlayer(ServerLevel world, DamageSource damageSource) { // Paper - ItemStack itemstack = new ItemStack(Items.ARMOR_STAND); - -+ if (this.level().purpurConfig.persistentDroppableEntityDisplayNames) - 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(world, damageSource); // Paper -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 7d83ad8a61f6aafbc063506c1858554f9b700b70..fd1bd4fb88d1bd4a0734db463dc1be640c736d34 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java -@@ -240,7 +240,13 @@ public class ItemFrame extends HangingEntity { - } - - if (dropSelf) { -- this.spawnAtLocation(world, this.getFrameItemStack()); -+ // Purpur start -+ final ItemStack itemFrame = this.getFrameItemStack(); -+ if (!this.level().purpurConfig.persistentDroppableEntityDisplayNames) { -+ itemFrame.set(DataComponents.CUSTOM_NAME, null); -+ } -+ this.spawnAtLocation(world, 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 fd0e78a2318e3950d011c17358245e107b38154a..0fcab828e81176323cbdf16c0ec714d9a2846ae5 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/Painting.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/Painting.java -@@ -179,7 +179,13 @@ public class Painting extends HangingEntity implements VariantHolder -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 df9cdcb9544a171a5a07c65ba0150933fb70d5fc..793bd6392ca3c3792306a20538233e4d7fb69b86 100644 ---- a/src/main/java/net/minecraft/world/item/NameTagItem.java -+++ b/src/main/java/net/minecraft/world/item/NameTagItem.java -@@ -23,6 +23,7 @@ public class NameTagItem extends Item { - if (!event.callEvent()) return InteractionResult.PASS; - 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 (user.level().purpurConfig.armorstandFixNametags && entity instanceof net.minecraft.world.entity.decoration.ArmorStand) entity.setCustomNameVisible(true); // Purpur - if (event.isPersistent() && newEntity instanceof Mob mob) { - // Paper end - Add PlayerNameEntityEvent - mob.setPersistenceRequired(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 4efb19a627b3735fe2ded2108576d76296b605f3..c6f4b6e14616aed688269c56aa9ddf11f097abaf 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -99,9 +99,11 @@ public class PurpurWorldConfig { - - public float armorstandStepHeight = 0.0F; - public boolean armorstandSetNameVisible = false; -+ 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/0100-Add-config-for-allowing-Endermen-to-despawn-even-whi.patch b/patches/server/0100-Add-config-for-allowing-Endermen-to-despawn-even-whi.patch deleted file mode 100644 index 8d4f84ebdd..0000000000 --- a/patches/server/0100-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 d2959976cf206f6c92ae90f103035e4760b9d955..c816521a5f73ccd04216ac0225945c6ef585869d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -468,7 +468,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 c6f4b6e14616aed688269c56aa9ddf11f097abaf..cdcc44e381dee9189f951e3f1984272b0f0f60b0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -862,6 +862,7 @@ public class PurpurWorldConfig { - public double endermanMaxHealth = 40.0D; - public double endermanScale = 1.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); -@@ -874,6 +875,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean endermiteRidable = false; diff --git a/patches/server/0101-Add-configurable-snowball-damage.patch b/patches/server/0101-Add-configurable-snowball-damage.patch deleted file mode 100644 index d4248b9896..0000000000 --- a/patches/server/0101-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 048ca5232d71f07d8ba7d3eaf0236660494c6b35..f712963fcd80535eee2bd04ec55ae1abdadef2bd 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -@@ -58,7 +58,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 cdcc44e381dee9189f951e3f1984272b0f0f60b0..3ec8e7ea1cf1eb5dd8fec5fefa7a77d68cb75632 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -339,6 +339,11 @@ public class PurpurWorldConfig { - }); - } - -+ public int snowballDamage = -1; -+ private void snowballSettings() { -+ snowballDamage = getInt("gameplay-mechanics.projectile-damage.snowball", snowballDamage); -+ } -+ - public boolean anvilAllowColors = false; - public boolean anvilColorsUseMiniMessage; - private void anvilSettings() { diff --git a/patches/server/0102-Changeable-Mob-Left-Handed-Chance.patch b/patches/server/0102-Changeable-Mob-Left-Handed-Chance.patch deleted file mode 100644 index 5ab06e4fe0..0000000000 --- a/patches/server/0102-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 1f253d5ace885423aa25ebdd986e1909a15ba018..06fbac8a968971d9ec3305e98b136d88be19a9f7 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -1422,7 +1422,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - attributemodifiable.addPermanentModifier(new AttributeModifier(Mob.RANDOM_SPAWN_BONUS_ID, randomsource.triangle(0.0D, 0.11485000000000001D), AttributeModifier.Operation.ADD_MULTIPLIED_BASE)); - } - -- this.setLeftHanded(randomsource.nextFloat() < 0.05F); -+ this.setLeftHanded(randomsource.nextFloat() < world.getLevel().purpurConfig.entityLeftHandedChance); // Purpur - return entityData; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 3ec8e7ea1cf1eb5dd8fec5fefa7a77d68cb75632..897fac3caeab051cf2634d4e8b3dca62b389acd8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -152,8 +152,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/0103-Add-boat-fall-damage-config.patch b/patches/server/0103-Add-boat-fall-damage-config.patch deleted file mode 100644 index d6e967c489..0000000000 --- a/patches/server/0103-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 5d4b0f8b621f62604f098da7bf0822d25b469547..51e58f447cc07224b2d9bbeff3719a719931a40c 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1439,7 +1439,16 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - if (this.isInvulnerableTo(world, source)) { - return false; - } else { -- if (source.is(net.minecraft.tags.DamageTypeTags.IS_FALL) && getRootVehicle() instanceof net.minecraft.world.entity.vehicle.AbstractMinecart && level().purpurConfig.minecartControllable && !level().purpurConfig.minecartControllableFallDamage) return false; // Purpur - Minecart settings and WASD controls -+ // Purpur start - Add boat fall damage config -+ if (source.is(net.minecraft.tags.DamageTypeTags.IS_FALL)) { -+ 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 - Add boat fall damage config - Entity entity = source.getEntity(); - - if (entity instanceof net.minecraft.world.entity.player.Player) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 897fac3caeab051cf2634d4e8b3dca62b389acd8..3bd6931d3b19bd9687cdaee2b245cd3f2e9df0bc 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -113,6 +113,7 @@ public class PurpurWorldConfig { - - public boolean useBetterMending = false; - public boolean boatEjectPlayersOnLand = false; -+ public boolean boatsDoFallDamage = false; - public boolean disableDropsOnCrammingDeath = false; - public boolean milkCuresBadOmen = true; - public double tridentLoyaltyVoidReturnHeight = 0.0D; -@@ -123,6 +124,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); - milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen); - tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight); diff --git a/patches/server/0104-Snow-Golem-rate-of-fire-config.patch b/patches/server/0104-Snow-Golem-rate-of-fire-config.patch deleted file mode 100644 index 8d2f240253..0000000000 --- a/patches/server/0104-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 aaa581c5fa7a14958b2a7c20996cf70ad4efff04..d13d8d251af99289a66934ef04c7dfb9bf0611a2 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -82,7 +82,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 - Ridables -- 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 - 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)); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 3bd6931d3b19bd9687cdaee2b245cd3f2e9df0bc..dec46168df0ca009bcc0a1abe3ff6f47f3d7a66e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1699,6 +1699,10 @@ public class PurpurWorldConfig { - 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; - private void snowGolemSettings() { - snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable); - snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater); -@@ -1712,6 +1716,10 @@ public class PurpurWorldConfig { - 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); - } - - public boolean snifferRidable = false; diff --git a/patches/server/0105-EMC-Configurable-disable-give-dropping.patch b/patches/server/0105-EMC-Configurable-disable-give-dropping.patch deleted file mode 100644 index dab7d4201c..0000000000 --- a/patches/server/0105-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 0d9de4c61c7b26a6ff37c12fde629161fd0c3d5a..2f7897744f4aea718170698881773e9031a58a51 100644 ---- a/src/main/java/net/minecraft/server/commands/GiveCommand.java -+++ b/src/main/java/net/minecraft/server/commands/GiveCommand.java -@@ -60,6 +60,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()) { - entityitem = entityplayer.drop(itemstack, false, false, false); // CraftBukkit - SPIGOT-2942: Add boolean to call event - if (entityitem != null) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index c61105bfb22531b728cb4f4af24c68625db5a99f..5feee7c130e71731051e610aeb0e8c3341bddb2e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -212,6 +212,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/0106-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch b/patches/server/0106-Option-for-Villager-Clerics-to-farm-Nether-Wart.patch deleted file mode 100644 index 9424f27ce9..0000000000 --- a/patches/server/0106-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 2ade08d1466660ee1787fa97908002ef56389712..4fa4ec34963730507253182cad1c2bf04090ad50 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 -@@ -41,6 +41,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)); -@@ -49,9 +50,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(); -@@ -82,6 +84,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; - } - -@@ -107,20 +110,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) { -@@ -136,7 +139,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 8508ac7de8cda3127b73e11ff4aee62502e65ead..b1544e028d5a9b84b944e1fb5a12bb163067fb54 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 -@@ -59,6 +59,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.getDefaultMaxStackSize() / 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 41f4107101bcd5d753b72cdbabe7946a1975c653..4475b406dde30e5be8ce9d2ff45f8d22d242690c 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 -@@ -74,8 +74,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 a0e0692d17760f440fe81d52887284c787e562db..ab9bebc07b5228dbc0d3ba4b0f7d1bbe41814c9b 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 ca1996ab3b998607c193ba427163a6a144990c97..66a8beddde806e04ca4d0e943734d6116b107bcc 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -227,7 +227,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)); -@@ -947,6 +947,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 8734ab1bd8299bbf43906d81a349c2a13e0981a7..3ca83269311cbc18c9ef3ce62cff6a2d4dc0a683 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java -+++ b/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java -@@ -31,7 +31,7 @@ public record VillagerProfession( - 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, -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index dec46168df0ca009bcc0a1abe3ff6f47f3d7a66e..53a740b2d85afc1fc09f91037aaff8b7e1caf5ea 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1920,6 +1920,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); -@@ -1936,6 +1938,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/0107-Toggle-for-Zombified-Piglin-death-always-counting-as.patch b/patches/server/0107-Toggle-for-Zombified-Piglin-death-always-counting-as.patch deleted file mode 100644 index e0689a3d70..0000000000 --- a/patches/server/0107-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 953df397ea261d417ada15db8d6ffc539ad07c03..c4bf422557fe6abbe882f575f19a9334c7a94fe5 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -@@ -150,7 +150,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; - } - -@@ -205,7 +205,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 53a740b2d85afc1fc09f91037aaff8b7e1caf5ea..09bf59c95f57f5beb718e74d99a6399317cf1222 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2189,6 +2189,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); -@@ -2204,5 +2205,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/0109-Configurable-default-collar-color.patch b/patches/server/0109-Configurable-default-collar-color.patch deleted file mode 100644 index fd7b73a7ee..0000000000 --- a/patches/server/0109-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 06b68fcf750a637e1f41eecacca2969cb55a8fa8..8d20b19b65616d8470542cc7337c8ddd7418be0b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -357,6 +357,14 @@ public class Cat extends TamableAnimal implements VariantHolder -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 7f62544ed66850c9545d8db9897236abd52cbd56..7926c11714b0f50fd02f2da4817c822fd5f60115 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -240,6 +240,7 @@ public class Phantom extends FlyingMob implements Enemy { - this.level().addParticle(ParticleTypes.MYCELIUM, this.getX() - (double) f3, this.getY() + (double) f5, this.getZ() - (double) f4, 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 8caef74b83984c6351f7366376b64be50b50a41c..739402d4e7b2b99ced75f62df33e4f4b4b9bdd6c 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 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); -@@ -1394,6 +1395,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/0111-Option-for-chests-to-open-even-with-a-solid-block-on.patch b/patches/server/0111-Option-for-chests-to-open-even-with-a-solid-block-on.patch deleted file mode 100644 index 2131b7c84f..0000000000 --- a/patches/server/0111-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 ca69a9fbd4942f9079aeaab7cead2d7a2c3b8659..54f351f1cbb50a5b1aa3167e3a0b10bb0456c1cf 100644 ---- a/src/main/java/net/minecraft/world/level/block/ChestBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/ChestBlock.java -@@ -336,6 +336,7 @@ public class ChestBlock extends AbstractChestBlock implements - } - - public 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 739402d4e7b2b99ced75f62df33e4f4b4b9bdd6c..012bdd66052eb30a185804a36e4cbde323bb1d68 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -376,6 +376,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/0112-Implement-TPSBar.patch b/patches/server/0112-Implement-TPSBar.patch deleted file mode 100644 index 543a5d6fd5..0000000000 --- a/patches/server/0112-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 719fbce359f8c2c52ee4e9da3dfe9566f58c0346..f01fff591efc92267d96084660f9e9688bd65795 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -258,6 +258,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 d55390d725f8798becb0b7b04485c99611e0e527..7ce4148a1fcd221bf22f4ad02a2fc32e007fa1da 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1158,6 +1158,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 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..6796fd6a936212a6eb768d9cf0fa5e74132c89e8 ---- /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.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() { -+ 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/0113-Striders-give-saddle-back.patch b/patches/server/0113-Striders-give-saddle-back.patch deleted file mode 100644 index 8866f5fd88..0000000000 --- a/patches/server/0113-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 b2f9e7dec710f9a135074d6b5eb438e127a7aaa9..b5bc19ea93058374ce3a01ed650b0396b5e4176d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Strider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java -@@ -485,6 +485,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 012bdd66052eb30a185804a36e4cbde323bb1d68..c0b9a7a7cd1e4acf151b12d308627d4ff2202018 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1814,6 +1814,7 @@ public class PurpurWorldConfig { - public double striderMaxHealth = 20.0D; - public double striderScale = 1.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); -@@ -1826,6 +1827,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean tadpoleRidable = false; diff --git a/patches/server/0114-PlayerBookTooLargeEvent.patch b/patches/server/0114-PlayerBookTooLargeEvent.patch deleted file mode 100644 index b9613cfab7..0000000000 --- a/patches/server/0114-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 6b460227a12ba275c02243a1fbafb87e3845cb14..2e714a4faf07e7ca18fa073ff6660c5d4f377507 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1256,6 +1256,10 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - 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 -+ int slot = packet.slot(); -+ ItemStack itemstack = Inventory.isHotbarSlot(slot) || slot == Inventory.SLOT_OFFHAND ? this.player.getInventory().getItem(slot) : ItemStack.EMPTY; -+ // Purpur end - for (final String page : pageList) { - final int byteLength = page.getBytes(java.nio.charset.StandardCharsets.UTF_8).length; - byteTotal += byteLength; -@@ -1280,7 +1284,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - } - - 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 - 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; - } diff --git a/patches/server/0115-Full-netherite-armor-grants-fire-resistance.patch b/patches/server/0115-Full-netherite-armor-grants-fire-resistance.patch deleted file mode 100644 index e692d00710..0000000000 --- a/patches/server/0115-Full-netherite-armor-grants-fire-resistance.patch +++ /dev/null @@ -1,52 +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 fee0ffaaf94c484c9272d3b5743b90bb70e0d08d..fc0e571b62a9bd40df2d3d066cf374e12004a6d8 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -356,6 +356,17 @@ public abstract class Player extends LivingEntity { - this.turtleHelmetTick(); - } - -+ // Purpur start -+ 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 -+ - this.cooldowns.tick(); - this.updatePlayerPose(); - if (this.currentImpulseContextResetGraceTime > 0) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index c0b9a7a7cd1e4acf151b12d308627d4ff2202018..19c6572a82de81b5a3aacad4bd77878d6c21ddc8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -285,6 +285,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/0116-Add-mobGriefing-bypass-to-everything-affected.patch b/patches/server/0116-Add-mobGriefing-bypass-to-everything-affected.patch deleted file mode 100644 index 1c0a2a101b..0000000000 --- a/patches/server/0116-Add-mobGriefing-bypass-to-everything-affected.patch +++ /dev/null @@ -1,684 +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 dfbb47c200dab7d99cde867c12420c67964867c2..c513e6de7d89992720fc4139969377c261571b72 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1901,7 +1901,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - boolean flag = false; - - if (this.dead && adversary instanceof WitherBoss) { // Paper -- if (worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (worldserver.purpurConfig.witherBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected - 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 06fbac8a968971d9ec3305e98b136d88be19a9f7..6836f0ddab9d05d146fc03c234b1b58829ec96c0 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -714,7 +714,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - Level world = this.level(); - - if (world instanceof ServerLevel worldserver) { -- if (this.canPickUpLoot() && this.isAlive() && !this.dead && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (this.canPickUpLoot() && this.isAlive() && !this.dead && (worldserver.purpurConfig.entitiesPickUpLootBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // Purpur - Add mobGriefing bypass to everything affected - 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 4fa4ec34963730507253182cad1c2bf04090ad50..88b7de6c0ab5bf6ba2af7b4cee0393879c2a4fdc 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 -@@ -48,7 +48,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 - Add mobGriefing bypass to everything affected - 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 7324da6b7dd2623ce394e3827ff77ef684a3b98b..a96f49641898aa16bdc99eed8a86497ddcd5a492 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 -@@ -33,7 +33,7 @@ public class BreakDoorGoal extends DoorInteractGoal { - - @Override - public boolean canUse() { -- return !super.canUse() ? false : (!getServerLevel((Entity) this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.isValidDifficulty(this.mob.level().getDifficulty()) && !this.isOpen()); -+ return !super.canUse() ? false : (!this.mob.level().purpurConfig.zombieBypassMobGriefing == !getServerLevel((Entity) this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.isValidDifficulty(this.mob.level().getDifficulty()) && !this.isOpen()); // Purpur - Add mobGriefing bypass to everything affected - } - - @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 32bb591371fe78ba10a2bc52389ef33978cbc0eb..6032eb2209d013b34c28eedd180583af3680fc69 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(), !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - fix wrong block state -+ if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition, blockState.getFluidState().createLegacyBlock(), !getServerLevel(this.level).purpurConfig.sheepBypassMobGriefing == !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - fix wrong block state // Purpur - Add mobGriefing bypass to everything affected - 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(), !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - Fix wrong block state -+ if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition1, Blocks.DIRT.defaultBlockState(), !getServerLevel(this.level).purpurConfig.sheepBypassMobGriefing == !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - Fix wrong block state // Purpur - Add mobGriefing bypass to everything affected - 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 9d245d08be61d7edee9138196ae3bf52023e3993..07e3e727e339765284095aa8fd6b5edd41dc4158 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 -@@ -41,7 +41,7 @@ public class RemoveBlockGoal extends MoveToBlockGoal { - - @Override - public boolean canUse() { -- if (!getServerLevel((Entity) this.removerMob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!getServerLevel(this.removerMob).purpurConfig.zombieBypassMobGriefing == !getServerLevel((Entity) this.removerMob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected - 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 ce597675e973eb0d7ce580a2b4dfd76e7bb586b1..7770288abae40de80954ce47a15e609ee1b3a8bc 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -1372,7 +1372,7 @@ public class Fox extends Animal implements VariantHolder { - } - - protected void onReachedTarget() { -- if (getServerLevel(Fox.this.level()).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (getServerLevel(Fox.this.level()).purpurConfig.foxBypassMobGriefing ^ getServerLevel(Fox.this.level()).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected - 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 0b4a1a8b0a41aaa083995c2059a9ce8757d44144..c90bbf17639157e38dc2cada25677e22d63be12d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java -@@ -636,7 +636,7 @@ public class Rabbit extends Animal implements VariantHolder { - @Override - public boolean canUse() { - if (this.nextStartTick <= 0) { -- if (!getServerLevel((Entity) this.rabbit).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!getServerLevel((Entity) this.rabbit).purpurConfig.rabbitBypassMobGriefing == !getServerLevel((Entity) this.rabbit).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected - 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 d13d8d251af99289a66934ef04c7dfb9bf0611a2..1c73b81053551b2aa2817f9629bc7b4050e8f070 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -132,7 +132,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - this.hurtServer(worldserver, this.damageSources().melting(), 1.0F); // CraftBukkit - DamageSources.ON_FIRE -> CraftEventFactory.MELTING - } - -- if (!worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!worldserver.purpurConfig.snowGolemBypassMobGriefing == !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected - 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 14feab288349f0fcd80b89466926b37d1087bf53..b7ba6c303826e35ee1261cb46f1e778af29a59e5 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 -@@ -592,7 +592,7 @@ public class EnderDragon extends Mob implements Enemy { - BlockState iblockdata = world.getBlockState(blockposition); - - if (!iblockdata.isAir() && !iblockdata.is(BlockTags.DRAGON_TRANSPARENT)) { -- if (world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && !iblockdata.is(BlockTags.DRAGON_IMMUNE)) { -+ if ((world.purpurConfig.enderDragonBypassMobGriefing ^ world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && !iblockdata.is(BlockTags.DRAGON_IMMUNE)) { // Purpur - Add mobGriefing bypass to everything affected - // CraftBukkit start - Add blocks to list rather than destroying them - // flag1 = worldserver.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 098ef0d6879e88009193c6dcedf1aa285980cbc6..297a67e8a8c1e314ad7f8d32d5bcacc3746b0b6c 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 -@@ -491,7 +491,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { - - if (this.destroyBlocksTick > 0) { - --this.destroyBlocksTick; -- if (this.destroyBlocksTick == 0 && world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (this.destroyBlocksTick == 0 && (world.purpurConfig.witherBypassMobGriefing ^ world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // Purpur - Add mobGriefing bypass to everything affected - boolean flag = false; - - j = Mth.floor(this.getBbWidth() / 2.0F + 1.0F); -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 c816521a5f73ccd04216ac0225945c6ef585869d..61a113074a6839d6dcb59019eefe9f532fb9cb2b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -516,7 +516,15 @@ public class EnderMan extends Monster implements NeutralMob { - @Override - public boolean canUse() { - if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - Add enderman and creeper griefing controls -- return this.enderman.getCarriedBlock() == null ? false : (!getServerLevel((Entity) this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0); -+ // Purpur start - Add mobGriefing bypass to everything affected -+ if (this.enderman.getCarriedBlock() == null) { -+ return false; -+ } -+ if (!getServerLevel((Entity) this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) == !this.enderman.level().purpurConfig.endermanBypassMobGriefing) { -+ return false; -+ } -+ return this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0; -+ // Purpur end - Add mobGriefing bypass to everything affected - } - - @Override -@@ -562,7 +570,15 @@ public class EnderMan extends Monster implements NeutralMob { - @Override - public boolean canUse() { - if (!enderman.level().purpurConfig.endermanAllowGriefing) return false; // Purpur - Add enderman and creeper griefing controls -- return this.enderman.getCarriedBlock() != null ? false : (!getServerLevel((Entity) this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0); -+ // Purpur start - Add mobGriefing bypass to everything affected -+ if (this.enderman.getCarriedBlock() != null) { -+ return false; -+ } -+ if (!getServerLevel((Entity) this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) == !this.enderman.level().purpurConfig.endermanBypassMobGriefing) { -+ return false; -+ } -+ return this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0; -+ // Purpur end - Add mobGriefing bypass to everything affected - } - - @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 76ee60fc1df5846393a5d387a514dfaf22e20229..05f859c3062c34f1fdb74c43423c1f5767180c1f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Evoker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Evoker.java -@@ -368,7 +368,7 @@ public class Evoker extends SpellcasterIllager { - } else { - ServerLevel worldserver = getServerLevel(Evoker.this.level()); - -- if (!worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (!worldserver.purpurConfig.evokerBypassMobGriefing == !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected - return false; - } else { - List list = worldserver.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 ecf37a99fa5e8919146ba73c7313998855ea0d88..b71aec7588a2a812cca71d89810ed765fcc33b9f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -182,7 +182,7 @@ public class Ravager extends Raider { - if (world instanceof ServerLevel) { - ServerLevel worldserver = (ServerLevel) world; - -- if (this.horizontalCollision && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (this.horizontalCollision && (worldserver.purpurConfig.ravagerBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // Purpur - Add mobGriefing bypass to everything affected - 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 191724aa48081017adf3db0b6ff99a77dd4ce68d..3159c61e1f34e95749661a711e4d97f7f5a04766 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java -@@ -193,12 +193,12 @@ public class Silverfish extends Monster { - - if (block instanceof InfestedBlock) { - // CraftBukkit start -- BlockState afterState = getServerLevel(world).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? iblockdata.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(world.getBlockState(blockposition1)); // Paper - fix wrong block state -+ BlockState afterState = (getServerLevel(world).purpurConfig.silverfishBypassMobGriefing ^ getServerLevel(world).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) ? iblockdata.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(world.getBlockState(blockposition1)); // Paper - fix wrong block state // Purpur - Add mobGriefing bypass to everything affected - if (!CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, blockposition1, afterState)) { // Paper - fix wrong block state - continue; - } - // CraftBukkit end -- if (getServerLevel(world).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { -+ if (getServerLevel(world).purpurConfig.silverfishBypassMobGriefing ^ getServerLevel(world).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected - world.destroyBlock(blockposition1, true, this.silverfish); - } else { - world.setBlock(blockposition1, ((InfestedBlock) block).hostStateByInfested(world.getBlockState(blockposition1)), 3); -@@ -236,7 +236,7 @@ public class Silverfish extends Monster { - } else { - RandomSource randomsource = this.mob.getRandom(); - -- if (getServerLevel((Entity) this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && randomsource.nextInt(reducedTickDelay(10)) == 0) { -+ if (getServerLevel((Entity) this.mob).purpurConfig.silverfishBypassMobGriefing ^ getServerLevel((Entity) this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && randomsource.nextInt(reducedTickDelay(10)) == 0) { // Purpur - Add mobGriefing bypass to everything affected - 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 1a40babc8f6b3f56377cb2af45e9d17d0028a77b..e1ae622ec56f1e011f8007c5b5b8a7032cd28102 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 -@@ -438,7 +438,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - - @Override - public boolean wantsToPickUp(ServerLevel world, ItemStack stack) { -- return world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack); -+ return (world.purpurConfig.piglinBypassMobGriefing ^ world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack); // Purpur - Add mobGriefing bypass to everything affected - } - - 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 2f00676f62478897ae4931ea06e047567c407535..59c71183e2c4edae72623f6aa662b807ba2093f2 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/LargeFireball.java -@@ -23,13 +23,13 @@ public class LargeFireball extends Fireball { - - public LargeFireball(EntityType type, Level world) { - super(type, world); -- this.isIncendiary = (world instanceof ServerLevel worldserver) && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit -+ this.isIncendiary = (world instanceof ServerLevel worldserver) && (worldserver.purpurConfig.fireballsBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)); // CraftBukkit // Purpur - Add mobGriefing bypass to everything affected - } - - public LargeFireball(Level world, LivingEntity owner, Vec3 velocity, int explosionPower) { - super(EntityType.FIREBALL, owner, velocity, world); - this.explosionPower = explosionPower; -- this.isIncendiary = (world instanceof ServerLevel worldserver) && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit -+ this.isIncendiary = (world instanceof ServerLevel worldserver) && (worldserver.purpurConfig.fireballsBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)); // CraftBukkit // Purpur - Add mobGriefing bypass to everything affected - } - - @Override -@@ -38,7 +38,7 @@ public class LargeFireball extends Fireball { - Level world = this.level(); - - if (world instanceof ServerLevel worldserver) { -- boolean flag = worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); -+ boolean flag = worldserver.purpurConfig.fireballsBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur - Add mobGriefing bypass to everything affected - - // 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 9a7b56b653848974e1194eb4f6d40cb99a96ff57..3a39eb8572cea2596edd26bef4a484c8cd78904d 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java -@@ -468,7 +468,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { - public boolean mayInteract(ServerLevel 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); // Purpur - Add mobGriefing bypass to everything affected - } - - public boolean mayBreak(ServerLevel world) { -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 bb159ea4baf208aab6d6fcfbbddacd5b089b55c8..f1786d17ce8ffd221674c887be01c7907f36f129 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/SmallFireball.java -@@ -30,7 +30,7 @@ public class SmallFireball extends Fireball { - super(EntityType.SMALL_FIREBALL, owner, velocity, world); - // CraftBukkit start - if (this.getOwner() != null && this.getOwner() instanceof Mob) { -- this.isIncendiary = (world instanceof ServerLevel worldserver) && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); -+ this.isIncendiary = (world instanceof ServerLevel worldserver) && (worldserver.purpurConfig.fireballsBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)); // Purpur - Add mobGriefing bypass to everything affected - } - // 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 cee1e4db2312efb4843c4b6dc18f4af10b91d304..65206bc0c3276fda449936cae88cc819a346e299 100644 ---- a/src/main/java/net/minecraft/world/entity/raid/Raider.java -+++ b/src/main/java/net/minecraft/world/entity/raid/Raider.java -@@ -345,7 +345,7 @@ public abstract class Raider extends PatrollingMonster { - } - - private boolean cannotPickUpBanner() { -- if (!getServerLevel(this.mob).getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items -+ if ((!this.mob.level().purpurConfig.pillagerBypassMobGriefing == !getServerLevel(this.mob).getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING)) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items // Purpur - Add mobGriefing bypass to everything affected - if (!this.mob.hasActiveRaid()) { - return true; - } else if (this.mob.getCurrentRaid().isOver()) { -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 1967ff3fcb94988be85985c4754904f0077de066..e1cfad4834fdee910bf261a60e2b76678a0fec6d 100644 ---- a/src/main/java/net/minecraft/world/level/block/CropBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java -@@ -180,7 +180,7 @@ public class CropBlock extends BushBlock implements BonemealableBlock { - protected 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 - Add EntityInsideBlockEvent - if (world instanceof ServerLevel worldserver) { -- if (entity instanceof Ravager && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit -+ if (entity instanceof Ravager && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !world.purpurConfig.ravagerBypassMobGriefing == !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Purpur - Add mobGriefing bypass to everything affected - worldserver.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 e744a117d2f25ad24c87263ba2ab7f760b6e594d..032b8252c3ff2fc111442610998e36a5c145ae13 100644 ---- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -@@ -112,7 +112,7 @@ public class FarmBlock extends Block { - 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 instanceof ServerLevel worldserver) { -- if (world.random.nextFloat() < fallDistance - 0.5F && entity instanceof LivingEntity && (entity instanceof Player || worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { -+ if (world.random.nextFloat() < fallDistance - 0.5F && entity instanceof LivingEntity && (entity instanceof Player || worldserver.purpurConfig.farmlandBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { // Purpur - Add mobGriefing bypass to everything affected - // 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 53f1a7ed6b4bd6e2d8460531226aabf249994c02..f91c845061fa632e53efb31c63cf0c67c9c2e86a 100644 ---- a/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java -@@ -76,7 +76,7 @@ public class PowderSnowBlock extends Block implements BucketPickup { - if (world instanceof ServerLevel worldserver) { - // CraftBukkit start - if (entity.isOnFire() && entity.mayInteract(worldserver, pos)) { -- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !(worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || entity instanceof Player))) { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !((worldserver.purpurConfig.powderSnowBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) || entity instanceof Player))) { // Purpur - Add mobGriefing bypass to everything affected - 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 df2028f53fd07551f8aa7eeb49f99ac0676d5fe0..30af2a98a52208c3a36dfaad474582806f86aede 100644 ---- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -@@ -222,7 +222,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 - Add mobGriefing bypass to everything affected - // Purpur end - Add turtle egg block options - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 19c6572a82de81b5a3aacad4bd77878d6c21ddc8..4f31d65962618a29b0a52ec7c51fba2267d63a05 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -121,6 +121,9 @@ public class PurpurWorldConfig { - public int raidCooldownSeconds = 0; - public int animalBreedingCooldownSeconds = 0; - public boolean persistentDroppableEntityDisplayNames = true; -+ public boolean entitiesPickUpLootBypassMobGriefing = false; -+ public boolean fireballsBypassMobGriefing = false; -+ public boolean projectilesBypassMobGriefing = false; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand); -@@ -132,6 +135,9 @@ public class PurpurWorldConfig { - 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); -+ entitiesPickUpLootBypassMobGriefing = getBoolean("gameplay-mechanics.entities-pick-up-loot-bypass-mob-griefing", entitiesPickUpLootBypassMobGriefing); -+ fireballsBypassMobGriefing = getBoolean("gameplay-mechanics.fireballs-bypass-mob-griefing", fireballsBypassMobGriefing); -+ projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing); - - } - -@@ -401,9 +407,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); - } -@@ -428,6 +436,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; -@@ -457,10 +470,12 @@ public class PurpurWorldConfig { - public boolean turtleEggsBreakFromExpOrbs = false; - public boolean turtleEggsBreakFromItems = false; - public boolean turtleEggsBreakFromMinecarts = false; -+ 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; -@@ -758,6 +773,7 @@ public class PurpurWorldConfig { - public double creeperScale = 1.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); -@@ -771,6 +787,7 @@ public class PurpurWorldConfig { - 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); -+ creeperBypassMobGriefing = getBoolean("mobs.creeper.bypass-mob-griefing", creeperBypassMobGriefing); - } - - public boolean dolphinRidable = false; -@@ -871,6 +888,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); -@@ -887,6 +905,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; -@@ -896,6 +915,7 @@ public class PurpurWorldConfig { - public double endermanScale = 1.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); -@@ -909,6 +929,7 @@ public class PurpurWorldConfig { - 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); -+ endermanBypassMobGriefing = getBoolean("mobs.enderman.bypass-mob-griefing", endermanBypassMobGriefing); - } - - public boolean endermiteRidable = false; -@@ -934,6 +955,7 @@ public class PurpurWorldConfig { - public boolean evokerControllable = true; - public double evokerMaxHealth = 24.0D; - public double evokerScale = 1.0D; -+ public boolean evokerBypassMobGriefing = false; - private void evokerSettings() { - evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable); - evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater); -@@ -945,6 +967,7 @@ public class PurpurWorldConfig { - } - evokerMaxHealth = getDouble("mobs.evoker.attributes.max_health", evokerMaxHealth); - evokerScale = Mth.clamp(getDouble("mobs.evoker.attributes.scale", evokerScale), 0.0625D, 16.0D); -+ evokerBypassMobGriefing = getBoolean("mobs.evoker.bypass-mob-griefing", evokerBypassMobGriefing); - } - - public boolean foxRidable = false; -@@ -954,6 +977,7 @@ public class PurpurWorldConfig { - public double foxScale = 1.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); -@@ -967,6 +991,7 @@ public class PurpurWorldConfig { - 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); -+ foxBypassMobGriefing = getBoolean("mobs.fox.bypass-mob-griefing", foxBypassMobGriefing); - } - - public boolean frogRidable = false; -@@ -1443,6 +1468,7 @@ public class PurpurWorldConfig { - public boolean piglinControllable = true; - public double piglinMaxHealth = 16.0D; - public double piglinScale = 1.0D; -+ public boolean piglinBypassMobGriefing = false; - private void piglinSettings() { - piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable); - piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater); -@@ -1454,6 +1480,7 @@ public class PurpurWorldConfig { - } - piglinMaxHealth = getDouble("mobs.piglin.attributes.max_health", piglinMaxHealth); - piglinScale = Mth.clamp(getDouble("mobs.piglin.attributes.scale", piglinScale), 0.0625D, 16.0D); -+ piglinBypassMobGriefing = getBoolean("mobs.piglin.bypass-mob-griefing", piglinBypassMobGriefing); - } - - public boolean piglinBruteRidable = false; -@@ -1479,6 +1506,7 @@ public class PurpurWorldConfig { - public boolean pillagerControllable = true; - public double pillagerMaxHealth = 24.0D; - public double pillagerScale = 1.0D; -+ public boolean pillagerBypassMobGriefing = false; - private void pillagerSettings() { - pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable); - pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater); -@@ -1490,6 +1518,7 @@ public class PurpurWorldConfig { - } - pillagerMaxHealth = getDouble("mobs.pillager.attributes.max_health", pillagerMaxHealth); - pillagerScale = Mth.clamp(getDouble("mobs.pillager.attributes.scale", pillagerScale), 0.0625D, 16.0D); -+ pillagerBypassMobGriefing = getBoolean("mobs.pillager.bypass-mob-griefing", pillagerBypassMobGriefing); - } - - public boolean polarBearRidable = false; -@@ -1541,6 +1570,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); -@@ -1555,6 +1585,7 @@ 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; -@@ -1562,6 +1593,7 @@ public class PurpurWorldConfig { - public boolean ravagerControllable = true; - public double ravagerMaxHealth = 100.0D; - public double ravagerScale = 1.0D; -+ public boolean ravagerBypassMobGriefing = false; - private void ravagerSettings() { - ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable); - ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater); -@@ -1573,6 +1605,7 @@ public class PurpurWorldConfig { - } - ravagerMaxHealth = getDouble("mobs.ravager.attributes.max_health", ravagerMaxHealth); - ravagerScale = Mth.clamp(getDouble("mobs.ravager.attributes.scale", ravagerScale), 0.0625D, 16.0D); -+ ravagerBypassMobGriefing = getBoolean("mobs.ravager.bypass-mob-griefing", ravagerBypassMobGriefing); - } - - public boolean salmonRidable = false; -@@ -1597,6 +1630,7 @@ public class PurpurWorldConfig { - public double sheepMaxHealth = 8.0D; - public double sheepScale = 1.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); -@@ -1609,6 +1643,7 @@ public class PurpurWorldConfig { - 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); -+ sheepBypassMobGriefing = getBoolean("mobs.sheep.bypass-mob-griefing", sheepBypassMobGriefing); - } - - public boolean shulkerRidable = false; -@@ -1636,6 +1671,7 @@ public class PurpurWorldConfig { - public double silverfishScale = 1.0D; - public double silverfishMovementSpeed = 0.25D; - public double silverfishAttackDamage = 1.0D; -+ public boolean silverfishBypassMobGriefing = false; - private void silverfishSettings() { - silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable); - silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater); -@@ -1649,6 +1685,7 @@ public class PurpurWorldConfig { - 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); -+ silverfishBypassMobGriefing = getBoolean("mobs.silverfish.bypass-mob-griefing", silverfishBypassMobGriefing); - } - - public boolean skeletonRidable = false; -@@ -1729,6 +1766,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); -@@ -1746,6 +1784,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; -@@ -1950,6 +1989,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); -@@ -1968,6 +2008,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; -@@ -2049,6 +2090,7 @@ public class PurpurWorldConfig { - public double witherScale = 1.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); -@@ -2067,6 +2109,7 @@ public class PurpurWorldConfig { - 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); -+ witherBypassMobGriefing = getBoolean("mobs.wither.bypass-mob-griefing", witherBypassMobGriefing); - } - - public boolean witherSkeletonRidable = false; -@@ -2145,6 +2188,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); -@@ -2161,6 +2205,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 zombieHorseRidable = false; diff --git a/patches/server/0117-Config-to-allow-Note-Block-sounds-when-blocked.patch b/patches/server/0117-Config-to-allow-Note-Block-sounds-when-blocked.patch deleted file mode 100644 index 47931c0fe7..0000000000 --- a/patches/server/0117-Config-to-allow-Note-Block-sounds-when-blocked.patch +++ /dev/null @@ -1,44 +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 6582db84c5307257f16c321453491cf24e40c9c7..f9015d4e478efeec8a796b7a897638f76064db20 100644 ---- a/src/main/java/net/minecraft/world/level/block/NoteBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NoteBlock.java -@@ -97,7 +97,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 4f31d65962618a29b0a52ec7c51fba2267d63a05..a71a380f20dad2f43183d128e7a487420248a671 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -124,6 +124,7 @@ public class PurpurWorldConfig { - public boolean entitiesPickUpLootBypassMobGriefing = false; - public boolean fireballsBypassMobGriefing = false; - public boolean projectilesBypassMobGriefing = false; -+ public boolean noteBlockIgnoreAbove = false; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand); -@@ -138,7 +139,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); - projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing); -- -+ noteBlockIgnoreAbove = getBoolean("gameplay-mechanics.note-block-ignore-above", noteBlockIgnoreAbove); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0118-Add-EntityTeleportHinderedEvent.patch b/patches/server/0118-Add-EntityTeleportHinderedEvent.patch deleted file mode 100644 index 3fe19cf38b..0000000000 --- a/patches/server/0118-Add-EntityTeleportHinderedEvent.patch +++ /dev/null @@ -1,121 +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/EndGatewayBlock.java b/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java -index af46f2885ead1e3ec1734504d8ba134c886e04fb..47ee0538c8ea94136b2416c324c8a264e54d2c09 100644 ---- a/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java -@@ -104,6 +104,13 @@ public class EndGatewayBlock extends BaseEntityBlock implements Portal { - TheEndGatewayBlockEntity tileentityendgateway = (TheEndGatewayBlockEntity) tileentity; - - if (!tileentityendgateway.isCoolingDown()) { -+ // 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()) { -+ return; -+ } -+ } -+ // Purpur end - entity.setAsInsidePortal(this, pos); - TheEndGatewayBlockEntity.triggerCooldown(world, pos, state, tileentityendgateway); - } -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 8cb4142562db0be1f1a7d961ec5a10d4abf31692..84ecb012cb0a47e47799dc73c7fadc75f462f47a 100644 ---- a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java -@@ -70,6 +70,13 @@ public class EndPortalBlock extends BaseEntityBlock implements Portal { - protected 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 - Add EntityInsideBlockEvent - if (entity.canUsePortal(false)) { -+ // Purpur start -+ if (world.purpurConfig.imposeTeleportRestrictionsOnEndPortals && (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_PORTAL).callEvent()) { -+ 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()), org.bukkit.PortalType.ENDER); // Paper - add portal type - world.getCraftServer().getPluginManager().callEvent(event); -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 3e9642e5236d9a1cc8e8f3b375d76810f4bc7c6c..5169cba4c43d80ce3597c57bf7d40bd0148ec8a0 100644 ---- a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -@@ -117,6 +117,13 @@ public class NetherPortalBlock extends Block implements Portal { - protected 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 - Add EntityInsideBlockEvent - if (entity.canUsePortal(false)) { -+ // Purpur start -+ if (world.purpurConfig.imposeTeleportRestrictionsOnNetherPortals && (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, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.NETHER_PORTAL).callEvent()) { -+ 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()), org.bukkit.PortalType.NETHER); // Paper - add portal type - world.getCraftServer().getPluginManager().callEvent(event); -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index c105d0cce462df46e106eb502355225b83be32b7..51772356a3c64da7b29fa204ffec03a70cd4406a 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -246,6 +246,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - boolean ignorePassengers = flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS); - // Don't allow teleporting between worlds while keeping passengers - if (flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS) && this.entity.isVehicle() && location.getWorld() != this.getWorld()) { -+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur - 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 16671636da1b6da62a0bdf0daab745fcd89143b7..ea8b1f01b2dd626c422649488c1e605a8054dbf2 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -1449,6 +1449,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - // Paper start - Teleport passenger API - // Don't allow teleporting between worlds while keeping passengers - if (ignorePassengers && entity.isVehicle() && location.getWorld() != this.getWorld()) { -+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur start - return false; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a71a380f20dad2f43183d128e7a487420248a671..07bbe52a858fa3f0cc9c9de709bf2ef013ac33f5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -125,6 +125,9 @@ public class PurpurWorldConfig { - public boolean fireballsBypassMobGriefing = false; - public boolean projectilesBypassMobGriefing = false; - public boolean noteBlockIgnoreAbove = false; -+ public boolean imposeTeleportRestrictionsOnGateways = false; -+ public boolean imposeTeleportRestrictionsOnNetherPortals = false; -+ public boolean imposeTeleportRestrictionsOnEndPortals = false; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - boatEjectPlayersOnLand = getBoolean("gameplay-mechanics.boat.eject-players-on-land", boatEjectPlayersOnLand); -@@ -140,6 +143,10 @@ public class PurpurWorldConfig { - fireballsBypassMobGriefing = getBoolean("gameplay-mechanics.fireballs-bypass-mob-griefing", fireballsBypassMobGriefing); - projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing); - 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); -+ - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0119-Farmland-trampling-changes.patch b/patches/server/0119-Farmland-trampling-changes.patch deleted file mode 100644 index 1369ff158e..0000000000 --- a/patches/server/0119-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 032b8252c3ff2fc111442610998e36a5c145ae13..fc5a755c558714442c1e12e88ee05764d6c1d9f4 100644 ---- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -@@ -127,12 +127,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 = ((LivingEntity) entity).getArmorSlots().iterator(); -+ if (armor.hasNext() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FEATHER_FALLING, 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 07bbe52a858fa3f0cc9c9de709bf2ef013ac33f5..3739561c633627ba3fe80f89bce84a243705f4bc 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -418,10 +418,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/0120-Movement-options-for-armor-stands.patch b/patches/server/0120-Movement-options-for-armor-stands.patch deleted file mode 100644 index bd2b53b73a..0000000000 --- a/patches/server/0120-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 c0e8739f0dd9290550872839eddca4b0090b603b..cfe26c762b2affa5050c175f6ab0306af6cfbe71 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2037,7 +2037,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - return this.isInWater() || flag; - } - -- void updateInWaterStateAndDoWaterCurrentPushing() { -+ public void updateInWaterStateAndDoWaterCurrentPushing() { // Purpur - package-private -> public - Movement options for armor stands - Entity entity = this.getVehicle(); - - if (entity instanceof AbstractBoat abstractboat) { -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 12ff824ffa81ea45f76337ec2b6d80b01047b698..e1c7a644dc922ca726cebf71ec2f0b159d6db289 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -114,10 +114,12 @@ public class ArmorStand extends LivingEntity { - private boolean noTickPoseDirty = 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 world) { - super(type, world); - if (world != null) this.canTick = world.paperConfig().entities.armorStands.tick; // Paper - Allow ArmorStands not to tick -+ if (world != null) this.canMovementTick = world.purpurConfig.armorstandMovement; // Purpur - Movement options for armor stands - this.handItems = NonNullList.withSize(2, ItemStack.EMPTY); - this.armorItems = NonNullList.withSize(4, ItemStack.EMPTY); - this.headPose = ArmorStand.DEFAULT_HEAD_POSE; -@@ -1015,4 +1017,18 @@ public class ArmorStand extends LivingEntity { - } - } - // 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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 3739561c633627ba3fe80f89bce84a243705f4bc..86b27b11f178be0cc05842ccb3ffe4ef14196855 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -100,10 +100,16 @@ public class PurpurWorldConfig { - 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; - 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/0121-Fix-stuck-in-portals.patch b/patches/server/0121-Fix-stuck-in-portals.patch deleted file mode 100644 index 1954d35e52..0000000000 --- a/patches/server/0121-Fix-stuck-in-portals.patch +++ /dev/null @@ -1,60 +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 303c9b2c6f1a62e40c5657248b727c14bb9c613a..92c0a7609e45a8cdb809ad7579f1713fc15675cf 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1678,6 +1678,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - worldserver1.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); - this.unsetRemoved(); - // CraftBukkit end -+ this.portalPos = io.papermc.paper.util.MCUtil.toBlockPosition(exit); // Purpur - Fix stuck in portals - this.setServerLevel(worldserver); - this.connection.internalTeleport(PositionMoveRotation.of(teleportTarget), teleportTarget.relatives()); // CraftBukkit - use internal teleport without event - this.connection.resetPosition(); -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index cfe26c762b2affa5050c175f6ab0306af6cfbe71..f15a8c14417b83b237dc7b9a11490116314cd4c6 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3465,14 +3465,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - return Vec3.directionFromRotation(this.getRotationVector()); - } - -+ public BlockPos portalPos = BlockPos.ZERO; // Purpur - 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 if (this.level.purpurConfig.entitiesCanUsePortals || this instanceof ServerPlayer) { // Purpur - Entities can use portals - if (this.portalProcess != null && this.portalProcess.isSamePortal(portal)) { - if (!this.portalProcess.isInsidePortalThisTick()) { - this.portalProcess.updateEntryPosition(pos.immutable()); - this.portalProcess.setAsInsidePortalThisTick(true); -+ this.portalPos = BlockPos.ZERO; // Purpur - Fix stuck in portals - } - } else { - this.portalProcess = new PortalProcessor(portal, pos.immutable()); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 86b27b11f178be0cc05842ccb3ffe4ef14196855..126d2689166579785735cf250ddf0838512ce81b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -327,6 +327,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); -@@ -342,6 +343,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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0122-Toggle-for-water-sensitive-mob-damage.patch b/patches/server/0122-Toggle-for-water-sensitive-mob-damage.patch deleted file mode 100644 index dd635a0156..0000000000 --- a/patches/server/0122-Toggle-for-water-sensitive-mob-damage.patch +++ /dev/null @@ -1,2430 +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 d556642fd07492aa52bfc0a2432ab3d16fe4f866..781f0adde9194162af57523aad4b73a358f622a7 100644 ---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java -+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java -@@ -44,6 +44,12 @@ public class GlowSquid extends Squid { - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.glowSquidMaxHealth); - } - // 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 - public boolean canFly() { - return this.level().purpurConfig.glowSquidsCanFly; -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 5f11c687df87015261d1d39c957e241fbeb5476a..79175bd42f37f10fae812e101dd5b09b209ebe30 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -114,6 +114,12 @@ public class Bat extends AmbientCreature { - this.getAttribute(Attributes.ATTACK_KNOCKBACK).setBaseValue(this.level().purpurConfig.batAttackKnockback); - } - // 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() && (float) this.tickCount % 10.0F == 0.0F; -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 121a4c58d4052eb0880f540e87beaf12a74eecd7..40137a98174cd0238d06c894373984a403f96fbc 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -186,7 +186,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); -- this.setPathfindingMalus(PathType.WATER, -1.0F); -+ if (isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // 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); -@@ -502,6 +502,11 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - return this.level().purpurConfig.beeBreedingTicks; - } - // Purpur end - Make entity breeding times configurable -+ @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 8d20b19b65616d8470542cc7337c8ddd7418be0b..75a961906658ff271bc0acf849648ce877c9e1be 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -138,6 +138,12 @@ public class Cat extends TamableAnimal implements VariantHolder { -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 d06fb7d714efc88af13e6357536d4c58a7ec3bb5..09ad3ca4fd5aaebb1f394e73bc030ffa798346ba 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -88,6 +88,12 @@ public class Chicken extends Animal { - return this.level().purpurConfig.chickenBreedingTicks; - } - // 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/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java -index dad2dc77afead53e0fa7f2f797ac3850279d5d40..478fb13b84609a545713371f903e4d6042747e94 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cod.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java -@@ -31,6 +31,12 @@ public class Cod extends AbstractSchoolingFish { - this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.codMaxHealth); - } - // 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/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index 4b27440624deda151f63f7ce4670fd805d24eaba..4d5d4f7bb24aded2933daa003b4c4bc38601e12d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -74,6 +74,12 @@ public class Cow extends Animal { - return this.level().purpurConfig.cowBreedingTicks; - } - // 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 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 5dfaed8ecf9438e8ea11cadd6c5638e0cc25cbb1..46862ebf302454a077a837da001abdceeffa1026 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -168,6 +168,12 @@ public class Dolphin extends AgeableWaterCreature { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.dolphinScale); - } - // 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 - @Nullable - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData entityData) { -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 7770288abae40de80954ce47a15e609ee1b3a8bc..a758f312ae4ed4a0404ca7cedbd8c7b88aef58f4 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -195,6 +195,12 @@ public class Fox extends Animal implements VariantHolder { - return this.level().purpurConfig.foxBreedingTicks; - } - // 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/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index 124b7d6881964039829313c52427e332e1ac526b..74971f19a646b78bff66d2543d80d9358fdd29c5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -86,6 +86,12 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ironGolemScale); - } - // 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 (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur - Ridables -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 28b544f4f2e0f30d831b57167fc3ea3ce28e2191..a8694e39954db18840e7c066ef6bcb58e3399c0b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -93,6 +93,12 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { - return this.level().purpurConfig.rabbitBreedingTicks; - } - // 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/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -index 88f0f9074db9a9afff55aa6bd17c38fa2e1e1f81..053c2773b52bd3399c3812452dbee2d1882d32a2 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -@@ -53,6 +53,12 @@ public class Salmon extends AbstractSchoolingFish implements VariantHolder, B - return this.level().purpurConfig.axolotlBreedingTicks; - } - // 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 world) { - return 0.0F; -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 c9d7be823a040e7de407537d247062821dca643a..4a8ac3d75a3c75eea8e361d7eb729f27ea807347 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 -@@ -116,6 +116,12 @@ public class Goat extends Animal { - return this.level().purpurConfig.goatBreedingTicks; - } - // 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(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 4cff4fcedc75a99ca71ae6a7c34d6f407665bf1b..6a511e01f8805451ce89d697568c23803b5985a8 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 -@@ -45,6 +45,12 @@ public class Donkey extends AbstractChestedHorse { - return this.level().purpurConfig.donkeyBreedingTicks; - } - // 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 - 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 cbeb7d1e834a4f0f120248bec619e34ca46f8069..c5ba0669b7e184ac42243a65a230fe325c3f84cc 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,12 @@ public class Horse extends AbstractHorse implements VariantHolder { - return this.level().purpurConfig.horseBreedingTicks; - } - // 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((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 ea396b5740ae1e7b2cf5356607835467b412d379..fa1bf3e2e11564b9528e056369a610cb5bbd25d0 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 -@@ -146,6 +146,12 @@ 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 - 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); -@@ -156,7 +156,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/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -index 9d8d836c93635f133a2ad6911cac9b8ac24f1f57..6de7a8cf41af2a4d2988f86341ba4329f9c45361 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java -@@ -51,6 +51,12 @@ public class CaveSpider extends Spider { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.caveSpiderScale); - } - // 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 world, Entity target) { - if (super.doHurtTarget(world, 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 4711f5e5d9f1738d739be68f586f744eba8621c9..76e0dcedc72576c0e0a85e09a762aa5e2b63c7ef 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -267,6 +267,12 @@ public class Creeper extends Monster { - return super.finalizeSpawn(world, difficulty, spawnReason, entityData); - } - // Purpur end - Charged creeper naturally spawn -+ // 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 - protected SoundEvent getHurtSound(DamageSource source) { -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 3f02cb4924817ed132d2b17b97a67f7d7bf57cb8..7651c29a6468e93bd10d58f76a86a68912509bd3 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -120,6 +120,12 @@ public class Drowned extends Zombie implements RangedAttackMob { - return level().purpurConfig.drownedJockeyTryExistingChickens; - } - // 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/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java -index 011327ea9bf9ef3497f2183ef149f8ec9bb29fab..d69716edb777155d0b52db25af64107e4657e03c 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java -@@ -52,6 +52,12 @@ public class ElderGuardian extends Guardian { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.elderGuardianScale); - } - // 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.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 61a113074a6839d6dcb59019eefe9f532fb9cb2b..a92c30b5d3079ce58e00b9824ec3a60e6b41f104 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -91,7 +91,7 @@ public class EnderMan extends Monster implements NeutralMob { - - public EnderMan(EntityType type, Level world) { - super(type, world); -- this.setPathfindingMalus(PathType.WATER, -1.0F); -+ if (isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // Purpur - Toggle for water sensitive mob damage - } - - // Purpur start - Ridables -@@ -288,7 +288,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/src/main/java/net/minecraft/world/entity/monster/Endermite.java b/src/main/java/net/minecraft/world/entity/monster/Endermite.java -index aeeb003cc43d6cff87a634099299ff30db7ed10b..fa4f25c7aece94a7468da1d7ed3cbd1abbce9640 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Endermite.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Endermite.java -@@ -62,6 +62,12 @@ public class Endermite extends Monster { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.endermiteScale); - } - // 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/src/main/java/net/minecraft/world/entity/monster/Evoker.java b/src/main/java/net/minecraft/world/entity/monster/Evoker.java -index 05f859c3062c34f1fdb74c43423c1f5767180c1f..8cc90983c0e4781b4cc73040ceba069e302f7664 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Evoker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Evoker.java -@@ -77,6 +77,12 @@ public class Evoker extends SpellcasterIllager { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.evokerScale); - } - // 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/src/main/java/net/minecraft/world/entity/monster/Ghast.java b/src/main/java/net/minecraft/world/entity/monster/Ghast.java -index b0d7bbca3c3a34f329698ee70ec0bea60c76b12e..67bc0ec362929099e741486e3424770478a046d9 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ghast.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ghast.java -@@ -85,6 +85,12 @@ public class Ghast extends FlyingMob implements Enemy { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ghastScale); - } - // 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/src/main/java/net/minecraft/world/entity/monster/Giant.java b/src/main/java/net/minecraft/world/entity/monster/Giant.java -index adc90a306fc9023f4a1cdfe9f58c39726086c849..4bf1414569685e9e8704216c9dcf585def88b2e5 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Giant.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Giant.java -@@ -59,6 +59,12 @@ public class Giant extends Monster { - this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.giantAttackDamage); - } - // 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).add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.ATTACK_DAMAGE, 50.0); - } -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 210e9dacd001e2e0c1a95d41c6a31aea6eab893b..01917ec09069be5d8b1051969367df1e53ff38ca 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,12 @@ public class Guardian extends Monster { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.guardianScale); - } - // 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 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 cd155b47369318f58ec55289a345f1bf28e2af14..43887b5aa533d456de5e66da2a3bef82aed58084 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Husk.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java -@@ -67,6 +67,12 @@ public class Husk extends Zombie { - return level().purpurConfig.huskJockeyTryExistingChickens; - } - // 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 - public static boolean checkHuskSpawnRules(EntityType type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) { - return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (EntitySpawnReason.isSpawner(spawnReason) || 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 6e3362324c81afacaaa0f9766e2d23a0a4f53ac3..d0fe85e7c44b78a078e6c2d6220849a336b079d3 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -@@ -85,6 +85,12 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.illusionerScale); - } - // 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/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -index 5ba64f9c653345b742624f18c4cf692d1e7880e1..8f0e9f854c4032c43cc1c9a8b087a5fa286326d8 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,12 @@ public class MagmaCube extends Slime { - return level().purpurConfig.magmaCubeAttackDamageCache; - } - // 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/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index 7926c11714b0f50fd02f2da4817c822fd5f60115..f09830c1c88f4b28f05e1647706a3c96a596ad1e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -120,6 +120,12 @@ public class Phantom extends FlyingMob implements Enemy { - } - // Purpur end - Ridables - -+ // Purpur start -+ @Override -+ public boolean isSensitiveToWater() { -+ return this.level().purpurConfig.phantomTakeDamageFromWater; -+ } -+ // Purpur end - // Purpur start - Phantoms attracted to crystals and crystals shoot phantoms - @Override - protected void dropFromLootTable(ServerLevel world, DamageSource damageSource, boolean causedByPlayer) { -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 78c01f0be0cc6689f68e2fcf3111d79abb5a59fb..0bc9ee0de4f30d4d57164e02992856a0bfa92041 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -@@ -91,6 +91,12 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.pillagerScale); - } - // 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/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -index b71aec7588a2a812cca71d89810ed765fcc33b9f..39081629685e3c7cd109626d1d61ce24b6afb860 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -105,6 +105,12 @@ public class Ravager extends Raider { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.ravagerScale); - } - // 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/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -index c04d6a5190f3db06601c3a0ab0ddaede3a9f88ac..2b2e68e1449606ecfe5c414834ce3e2c6912d820 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -120,7 +120,12 @@ public class Shulker extends AbstractGolem implements VariantHolder type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random - ) { -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 b5bc19ea93058374ce3a01ed650b0396b5e4176d..420118b1df271377f69e8e2bab8a62623382c819 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Strider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java -@@ -91,7 +91,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(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); -@@ -443,7 +443,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - - @Override - public boolean isSensitiveToWater() { -- return true; -+ return this.level().purpurConfig.striderTakeDamageFromWater; // Purpur - Toggle for water sensitive mob damage - } - - @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 906934076dd721a39f8ee960fc7c0d1058b66ae2..1630074bf8065a7ecf49d426f8d5fe5e109b1d2c 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vex.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java -@@ -110,6 +110,12 @@ public class Vex extends Monster implements TraceableEntity { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.vexScale); - } - // 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 % Vex.TICKS_PER_FLAP == 0; -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 7f414f74c315db67a8e8a3d3636811abe733f62a..5693081f061dcefbfda374c26a6ab5be0c2e2fcc 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -@@ -79,6 +79,12 @@ public class Vindicator extends AbstractIllager { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.vindicatorScale); - } - // 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/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java -index 9b2d76722385ccf9d0ace747339ca7705ca41f4f..f77ce3de847ea5c7f40bfa6bd4bba254dc863bd0 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Witch.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java -@@ -81,6 +81,12 @@ public class Witch extends Raider implements RangedAttackMob { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.witchScale); - } - // 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/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -index feb1b516c7ac7200e7cebeea739369426e87bf27..1a864ebb6257f34e8fb7a543d6da19cfa96bb7d0 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -@@ -60,6 +60,12 @@ public class WitherSkeleton extends AbstractSkeleton { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.witherSkeletonScale); - } - // 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/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index b5c4e127298795567ea4f35aa5a209ee6e1629b3..1c2c0f94811f934804cfb0be15fa55c0281ad0ba 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -@@ -109,6 +109,12 @@ public class Zoglin extends Monster implements HoglinBase { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.zoglinScale); - } - // 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/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index 473ab54ac90f00bf5baf6b53190fa0f4f762700b..09b21cf02a07e1eb6a0aa2c6880d4106fe9e0d77 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -147,6 +147,12 @@ public class Zombie extends Monster { - return level().purpurConfig.zombieJockeyTryExistingChickens; - } - // 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/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index 36a074627a95886d38bd5a262dddcebfe32d1ba6..3f3e0dd479afad1ca73ad9f76ed673fa399955c6 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -129,6 +129,12 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - return level().purpurConfig.zombieVillagerJockeyTryExistingChickens; - } - // 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/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index c4bf422557fe6abbe882f575f19a9334c7a94fe5..3070f4484276f8ea6b95134abda6409e58f1161a 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -@@ -103,6 +103,12 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - return level().purpurConfig.zombifiedPiglinJockeyTryExistingChickens; - } - // 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 - 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 d7bef344f1d18aa6037b3805da07353c54c82142..534626336e45da2c67cb023f4e5feea59daa8b5d 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 -@@ -106,6 +106,12 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - return this.level().purpurConfig.hoglinBreedingTicks; - } - // 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 - @Override - public boolean canBeLeashed() { - return true; -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 e1ae622ec56f1e011f8007c5b5b8a7032cd28102..f0b0642571a242f9c22febbafba587f1ca6caaaa 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,12 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.piglinScale); - } - // 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 - 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 a0fa32a1217bbdae2c91e5a51598d7bd555ca1eb..0e77e9e4a4fd14b5cecc377a03f0d6b2c9df039f 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 -@@ -89,6 +89,12 @@ public class PiglinBrute extends AbstractPiglin { - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.piglinBruteScale); - } - // 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/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 66a8beddde806e04ca4d0e943734d6116b107bcc..e33a29c486d50c5f22888bcb1979830143c8b06d 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -193,6 +193,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - return level().purpurConfig.villagerCanBeLeashed; - } - // Purpur end - Allow leashing villagers -+ // 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(); // 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 0066e413d1fa92ecd5573c00195aa2f1412e665e..455390bd5350282d1a6c9b25e9cdbd4a620b136c 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -107,6 +107,12 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - return level().purpurConfig.wanderingTraderCanBeLeashed; - } - // Purpur end - Allow leashing villagers -+ // 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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 126d2689166579785735cf250ddf0838512ce81b..9ab4dfe8cccbb8e4cc200930059acde1fb0341a6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -549,12 +549,14 @@ public class PurpurWorldConfig { - public double axolotlMaxHealth = 14.0D; - public double axolotlScale = 1.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); - 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); - } - - public boolean batRidable = false; -@@ -570,6 +572,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); -@@ -589,6 +592,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; -@@ -598,6 +602,7 @@ public class PurpurWorldConfig { - public double beeMaxHealth = 10.0D; - public double beeScale = 1.0D; - public int beeBreedingTicks = 6000; -+ public boolean beeTakeDamageFromWater = true; - private void beeSettings() { - beeRidable = getBoolean("mobs.bee.ridable", beeRidable); - beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater); -@@ -611,6 +616,7 @@ public class PurpurWorldConfig { - 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); -+ beeTakeDamageFromWater = getBoolean("mobs.bee.takes-damage-from-water", beeTakeDamageFromWater); - } - - public boolean blazeRidable = false; -@@ -619,6 +625,7 @@ public class PurpurWorldConfig { - public double blazeMaxY = 320D; - public double blazeMaxHealth = 20.0D; - public double blazeScale = 1.0D; -+ public boolean blazeTakeDamageFromWater = true; - private void blazeSettings() { - blazeRidable = getBoolean("mobs.blaze.ridable", blazeRidable); - blazeRidableInWater = getBoolean("mobs.blaze.ridable-in-water", blazeRidableInWater); -@@ -631,6 +638,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean boggedRidable = false; -@@ -675,6 +683,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); -@@ -695,6 +704,7 @@ public class PurpurWorldConfig { - } catch (IllegalArgumentException ignore) { - catDefaultCollarColor = DyeColor.RED; - } -+ catTakeDamageFromWater = getBoolean("mobs.cat.takes-damage-from-water", catTakeDamageFromWater); - } - - public boolean caveSpiderRidable = false; -@@ -702,6 +712,7 @@ public class PurpurWorldConfig { - public boolean caveSpiderControllable = true; - public double caveSpiderMaxHealth = 12.0D; - public double caveSpiderScale = 1.0D; -+ public boolean caveSpiderTakeDamageFromWater = false; - private void caveSpiderSettings() { - caveSpiderRidable = getBoolean("mobs.cave_spider.ridable", caveSpiderRidable); - caveSpiderRidableInWater = getBoolean("mobs.cave_spider.ridable-in-water", caveSpiderRidableInWater); -@@ -713,6 +724,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean chickenRidable = false; -@@ -722,6 +734,7 @@ public class PurpurWorldConfig { - public double chickenScale = 1.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); -@@ -735,12 +748,14 @@ public class PurpurWorldConfig { - 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); - } - - public boolean codRidable = false; - public boolean codControllable = true; - public double codMaxHealth = 3.0D; - public double codScale = 1.0D; -+ public boolean codTakeDamageFromWater = false; - private void codSettings() { - codRidable = getBoolean("mobs.cod.ridable", codRidable); - codControllable = getBoolean("mobs.cod.controllable", codControllable); -@@ -751,6 +766,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean cowRidable = false; -@@ -760,6 +776,7 @@ public class PurpurWorldConfig { - public double cowScale = 1.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); -@@ -773,6 +790,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean creakingRidable = false; -@@ -796,6 +814,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); -@@ -810,6 +829,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; -@@ -820,6 +840,7 @@ public class PurpurWorldConfig { - public double dolphinMaxHealth = 10.0D; - public double dolphinScale = 1.0D; - public boolean dolphinDisableTreasureSearching = false; -+ public boolean dolphinTakeDamageFromWater = false; - private void dolphinSettings() { - dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable); - dolphinControllable = getBoolean("mobs.dolphin.controllable", dolphinControllable); -@@ -834,6 +855,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean donkeyRidableInWater = false; -@@ -844,6 +866,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) { -@@ -860,6 +883,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; -@@ -871,6 +895,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); -@@ -886,12 +911,14 @@ 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 double elderGuardianScale = 1.0D; -+ public boolean elderGuardianTakeDamageFromWater = false; - private void elderGuardianSettings() { - elderGuardianRidable = getBoolean("mobs.elder_guardian.ridable", elderGuardianRidable); - elderGuardianControllable = getBoolean("mobs.elder_guardian.controllable", elderGuardianControllable); -@@ -902,6 +929,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean enderDragonRidable = false; -@@ -911,6 +939,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); -@@ -928,6 +957,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; -@@ -938,6 +968,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); -@@ -952,6 +983,7 @@ 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; -@@ -959,6 +991,7 @@ public class PurpurWorldConfig { - public boolean endermiteControllable = true; - public double endermiteMaxHealth = 8.0D; - public double endermiteScale = 1.0D; -+ public boolean endermiteTakeDamageFromWater = false; - private void endermiteSettings() { - endermiteRidable = getBoolean("mobs.endermite.ridable", endermiteRidable); - endermiteRidableInWater = getBoolean("mobs.endermite.ridable-in-water", endermiteRidableInWater); -@@ -970,6 +1003,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean evokerRidable = false; -@@ -978,6 +1012,7 @@ public class PurpurWorldConfig { - public double evokerMaxHealth = 24.0D; - public double evokerScale = 1.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); -@@ -990,6 +1025,7 @@ public class PurpurWorldConfig { - evokerMaxHealth = getDouble("mobs.evoker.attributes.max_health", evokerMaxHealth); - evokerScale = Mth.clamp(getDouble("mobs.evoker.attributes.scale", evokerScale), 0.0625D, 16.0D); - evokerBypassMobGriefing = getBoolean("mobs.evoker.bypass-mob-griefing", evokerBypassMobGriefing); -+ evokerTakeDamageFromWater = getBoolean("mobs.evoker.takes-damage-from-water", evokerTakeDamageFromWater); - } - - public boolean foxRidable = false; -@@ -1000,6 +1036,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); -@@ -1014,6 +1051,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; -@@ -1035,6 +1073,7 @@ public class PurpurWorldConfig { - public double ghastMaxY = 320D; - public double ghastMaxHealth = 10.0D; - public double ghastScale = 1.0D; -+ public boolean ghastTakeDamageFromWater = false; - private void ghastSettings() { - ghastRidable = getBoolean("mobs.ghast.ridable", ghastRidable); - ghastRidableInWater = getBoolean("mobs.ghast.ridable-in-water", ghastRidableInWater); -@@ -1047,6 +1086,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean giantRidable = false; -@@ -1060,6 +1100,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); -@@ -1081,6 +1122,7 @@ 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; -@@ -1088,12 +1130,14 @@ public class PurpurWorldConfig { - public double glowSquidMaxHealth = 10.0D; - public double glowSquidScale = 1.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); - 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); - } - - public boolean goatRidable = false; -@@ -1102,6 +1146,7 @@ public class PurpurWorldConfig { - public double goatMaxHealth = 10.0D; - public double goatScale = 1.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); -@@ -1109,12 +1154,14 @@ public class PurpurWorldConfig { - 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); - } - - public boolean guardianRidable = false; - public boolean guardianControllable = true; - public double guardianMaxHealth = 30.0D; - public double guardianScale = 1.0D; -+ public boolean guardianTakeDamageFromWater = false; - private void guardianSettings() { - guardianRidable = getBoolean("mobs.guardian.ridable", guardianRidable); - guardianControllable = getBoolean("mobs.guardian.controllable", guardianControllable); -@@ -1125,6 +1172,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean hoglinRidable = false; -@@ -1133,6 +1181,7 @@ public class PurpurWorldConfig { - public double hoglinMaxHealth = 40.0D; - public double hoglinScale = 1.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); -@@ -1145,6 +1194,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean horseRidableInWater = false; -@@ -1155,6 +1205,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) { -@@ -1171,6 +1222,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; -@@ -1182,6 +1234,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); -@@ -1197,6 +1250,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; -@@ -1206,6 +1260,7 @@ public class PurpurWorldConfig { - public double illusionerFollowRange = 18.0D; - public double illusionerMaxHealth = 32.0D; - public double illusionerScale = 1.0D; -+ public boolean illusionerTakeDamageFromWater = false; - private void illusionerSettings() { - illusionerRidable = getBoolean("mobs.illusioner.ridable", illusionerRidable); - illusionerRidableInWater = getBoolean("mobs.illusioner.ridable-in-water", illusionerRidableInWater); -@@ -1223,6 +1278,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean ironGolemRidable = false; -@@ -1231,6 +1287,7 @@ public class PurpurWorldConfig { - public boolean ironGolemCanSwim = false; - public double ironGolemMaxHealth = 100.0D; - public double ironGolemScale = 1.0D; -+ public boolean ironGolemTakeDamageFromWater = false; - private void ironGolemSettings() { - ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable); - ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater); -@@ -1243,6 +1300,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean llamaRidable = false; -@@ -1255,6 +1313,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); -@@ -1273,6 +1332,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; -@@ -1282,6 +1342,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); -@@ -1295,6 +1356,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; -@@ -1303,6 +1365,7 @@ public class PurpurWorldConfig { - public double mooshroomMaxHealth = 10.0D; - public double mooshroomScale = 1.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); -@@ -1315,6 +1378,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean muleRidableInWater = false; -@@ -1325,6 +1389,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) { -@@ -1341,6 +1406,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; -@@ -1349,6 +1415,7 @@ public class PurpurWorldConfig { - public double ocelotMaxHealth = 10.0D; - public double ocelotScale = 1.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); -@@ -1361,6 +1428,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean pandaRidable = false; -@@ -1369,6 +1437,7 @@ public class PurpurWorldConfig { - public double pandaMaxHealth = 20.0D; - public double pandaScale = 1.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); -@@ -1381,6 +1450,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean parrotRidable = false; -@@ -1389,6 +1459,7 @@ public class PurpurWorldConfig { - public double parrotMaxY = 320D; - public double parrotMaxHealth = 6.0D; - public double parrotScale = 1.0D; -+ public boolean parrotTakeDamageFromWater = false; - private void parrotSettings() { - parrotRidable = getBoolean("mobs.parrot.ridable", parrotRidable); - parrotRidableInWater = getBoolean("mobs.parrot.ridable-in-water", parrotRidableInWater); -@@ -1401,6 +1472,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean phantomRidable = false; -@@ -1427,6 +1499,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); -@@ -1461,6 +1534,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; -@@ -1470,6 +1544,7 @@ public class PurpurWorldConfig { - public double pigScale = 1.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); -@@ -1483,6 +1558,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean piglinRidable = false; -@@ -1491,6 +1567,7 @@ public class PurpurWorldConfig { - public double piglinMaxHealth = 16.0D; - public double piglinScale = 1.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); -@@ -1503,6 +1580,7 @@ public class PurpurWorldConfig { - piglinMaxHealth = getDouble("mobs.piglin.attributes.max_health", piglinMaxHealth); - piglinScale = Mth.clamp(getDouble("mobs.piglin.attributes.scale", piglinScale), 0.0625D, 16.0D); - piglinBypassMobGriefing = getBoolean("mobs.piglin.bypass-mob-griefing", piglinBypassMobGriefing); -+ piglinTakeDamageFromWater = getBoolean("mobs.piglin.takes-damage-from-water", piglinTakeDamageFromWater); - } - - public boolean piglinBruteRidable = false; -@@ -1510,6 +1588,7 @@ public class PurpurWorldConfig { - public boolean piglinBruteControllable = true; - public double piglinBruteMaxHealth = 50.0D; - public double piglinBruteScale = 1.0D; -+ public boolean piglinBruteTakeDamageFromWater = false; - private void piglinBruteSettings() { - piglinBruteRidable = getBoolean("mobs.piglin_brute.ridable", piglinBruteRidable); - piglinBruteRidableInWater = getBoolean("mobs.piglin_brute.ridable-in-water", piglinBruteRidableInWater); -@@ -1521,6 +1600,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean pillagerRidable = false; -@@ -1529,6 +1609,7 @@ public class PurpurWorldConfig { - public double pillagerMaxHealth = 24.0D; - public double pillagerScale = 1.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); -@@ -1541,6 +1622,7 @@ public class PurpurWorldConfig { - pillagerMaxHealth = getDouble("mobs.pillager.attributes.max_health", pillagerMaxHealth); - pillagerScale = Mth.clamp(getDouble("mobs.pillager.attributes.scale", pillagerScale), 0.0625D, 16.0D); - pillagerBypassMobGriefing = getBoolean("mobs.pillager.bypass-mob-griefing", pillagerBypassMobGriefing); -+ pillagerTakeDamageFromWater = getBoolean("mobs.pillager.takes-damage-from-water", pillagerTakeDamageFromWater); - } - - public boolean polarBearRidable = false; -@@ -1551,6 +1633,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); -@@ -1566,12 +1649,14 @@ public class PurpurWorldConfig { - Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.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); - } - - public boolean pufferfishRidable = false; - public boolean pufferfishControllable = true; - public double pufferfishMaxHealth = 3.0D; - public double pufferfishScale = 1.0D; -+ public boolean pufferfishTakeDamageFromWater = false; - private void pufferfishSettings() { - pufferfishRidable = getBoolean("mobs.pufferfish.ridable", pufferfishRidable); - pufferfishControllable = getBoolean("mobs.pufferfish.controllable", pufferfishControllable); -@@ -1582,6 +1667,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean rabbitRidable = false; -@@ -1593,6 +1679,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); -@@ -1608,6 +1695,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; -@@ -1616,6 +1704,7 @@ public class PurpurWorldConfig { - public double ravagerMaxHealth = 100.0D; - public double ravagerScale = 1.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); -@@ -1628,12 +1717,14 @@ public class PurpurWorldConfig { - ravagerMaxHealth = getDouble("mobs.ravager.attributes.max_health", ravagerMaxHealth); - ravagerScale = Mth.clamp(getDouble("mobs.ravager.attributes.scale", ravagerScale), 0.0625D, 16.0D); - 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 double salmonScale = 1.0D; -+ public boolean salmonTakeDamageFromWater = false; - private void salmonSettings() { - salmonRidable = getBoolean("mobs.salmon.ridable", salmonRidable); - salmonControllable = getBoolean("mobs.salmon.controllable", salmonControllable); -@@ -1644,6 +1735,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean sheepRidable = false; -@@ -1653,6 +1745,7 @@ public class PurpurWorldConfig { - public double sheepScale = 1.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); -@@ -1666,6 +1759,7 @@ public class PurpurWorldConfig { - sheepScale = Mth.clamp(getDouble("mobs.sheep.attributes.scale", sheepScale), 0.0625D, 16.0D); - 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; -@@ -1673,6 +1767,7 @@ public class PurpurWorldConfig { - public boolean shulkerControllable = true; - public double shulkerMaxHealth = 30.0D; - public double shulkerScale = 1.0D; -+ public boolean shulkerTakeDamageFromWater = false; - private void shulkerSettings() { - shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable); - shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater); -@@ -1684,6 +1779,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean silverfishRidable = false; -@@ -1694,6 +1790,7 @@ public class PurpurWorldConfig { - public double silverfishMovementSpeed = 0.25D; - public double silverfishAttackDamage = 1.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); -@@ -1708,6 +1805,7 @@ public class PurpurWorldConfig { - silverfishMovementSpeed = getDouble("mobs.silverfish.attributes.movement_speed", silverfishMovementSpeed); - silverfishAttackDamage = getDouble("mobs.silverfish.attributes.attack_damage", silverfishAttackDamage); - silverfishBypassMobGriefing = getBoolean("mobs.silverfish.bypass-mob-griefing", silverfishBypassMobGriefing); -+ silverfishTakeDamageFromWater = getBoolean("mobs.silverfish.takes-damage-from-water", silverfishTakeDamageFromWater); - } - - public boolean skeletonRidable = false; -@@ -1715,6 +1813,7 @@ public class PurpurWorldConfig { - public boolean skeletonControllable = true; - public double skeletonMaxHealth = 20.0D; - public double skeletonScale = 1.0D; -+ public boolean skeletonTakeDamageFromWater = false; - private void skeletonSettings() { - skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable); - skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater); -@@ -1726,6 +1825,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean skeletonHorseRidable = false; -@@ -1737,6 +1837,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() { - skeletonHorseRidable = getBoolean("mobs.skeleton_horse.ridable", skeletonHorseRidable); - skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater); -@@ -1753,6 +1854,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; -@@ -1762,6 +1864,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); -@@ -1775,6 +1878,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; -@@ -1789,6 +1893,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); -@@ -1807,6 +1912,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; -@@ -1831,6 +1937,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); -@@ -1844,6 +1951,7 @@ 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; -@@ -1851,6 +1959,7 @@ public class PurpurWorldConfig { - public boolean spiderControllable = true; - public double spiderMaxHealth = 16.0D; - public double spiderScale = 1.0D; -+ public boolean spiderTakeDamageFromWater = false; - private void spiderSettings() { - spiderRidable = getBoolean("mobs.spider.ridable", spiderRidable); - spiderRidableInWater = getBoolean("mobs.spider.ridable-in-water", spiderRidableInWater); -@@ -1862,6 +1971,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean strayRidable = false; -@@ -1869,6 +1979,7 @@ public class PurpurWorldConfig { - public boolean strayControllable = true; - public double strayMaxHealth = 20.0D; - public double strayScale = 1.0D; -+ public boolean strayTakeDamageFromWater = false; - private void straySettings() { - strayRidable = getBoolean("mobs.stray.ridable", strayRidable); - strayRidableInWater = getBoolean("mobs.stray.ridable-in-water", strayRidableInWater); -@@ -1880,6 +1991,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean striderRidable = false; -@@ -1889,6 +2001,7 @@ public class PurpurWorldConfig { - public double striderScale = 1.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); -@@ -1902,6 +2015,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean tadpoleRidable = false; -@@ -1923,6 +2037,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); -@@ -1941,12 +2056,14 @@ 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 double tropicalFishScale = 1.0D; -+ public boolean tropicalFishTakeDamageFromWater = false; - private void tropicalFishSettings() { - tropicalFishRidable = getBoolean("mobs.tropical_fish.ridable", tropicalFishRidable); - tropicalFishControllable = getBoolean("mobs.tropical_fish.controllable", tropicalFishControllable); -@@ -1957,6 +2074,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean turtleRidable = false; -@@ -1965,6 +2083,7 @@ public class PurpurWorldConfig { - public double turtleMaxHealth = 30.0D; - public double turtleScale = 1.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); -@@ -1977,6 +2096,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean vexRidable = false; -@@ -1985,6 +2105,7 @@ public class PurpurWorldConfig { - public double vexMaxY = 320D; - public double vexMaxHealth = 14.0D; - public double vexScale = 1.0D; -+ public boolean vexTakeDamageFromWater = false; - private void vexSettings() { - vexRidable = getBoolean("mobs.vex.ridable", vexRidable); - vexRidableInWater = getBoolean("mobs.vex.ridable-in-water", vexRidableInWater); -@@ -1997,6 +2118,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean villagerRidable = false; -@@ -2012,6 +2134,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); -@@ -2031,6 +2154,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; -@@ -2039,6 +2163,7 @@ public class PurpurWorldConfig { - public double vindicatorMaxHealth = 24.0D; - public double vindicatorScale = 1.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); -@@ -2051,6 +2176,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean wanderingTraderRidable = false; -@@ -2061,6 +2187,7 @@ public class PurpurWorldConfig { - public boolean wanderingTraderFollowEmeraldBlock = false; - public double wanderingTraderTemptRange = 10.0D; - 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); -@@ -2075,6 +2202,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean wardenRidable = false; -@@ -2091,6 +2219,7 @@ public class PurpurWorldConfig { - public boolean witchControllable = true; - public double witchMaxHealth = 26.0D; - public double witchScale = 1.0D; -+ public boolean witchTakeDamageFromWater = false; - private void witchSettings() { - witchRidable = getBoolean("mobs.witch.ridable", witchRidable); - witchRidableInWater = getBoolean("mobs.witch.ridable-in-water", witchRidableInWater); -@@ -2102,6 +2231,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean witherRidable = false; -@@ -2113,6 +2243,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); -@@ -2132,6 +2263,7 @@ 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; -@@ -2139,6 +2271,7 @@ public class PurpurWorldConfig { - public boolean witherSkeletonControllable = true; - public double witherSkeletonMaxHealth = 20.0D; - public double witherSkeletonScale = 1.0D; -+ public boolean witherSkeletonTakeDamageFromWater = false; - private void witherSkeletonSettings() { - witherSkeletonRidable = getBoolean("mobs.wither_skeleton.ridable", witherSkeletonRidable); - witherSkeletonRidableInWater = getBoolean("mobs.wither_skeleton.ridable-in-water", witherSkeletonRidableInWater); -@@ -2150,6 +2283,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean wolfRidable = false; -@@ -2161,6 +2295,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); -@@ -2180,6 +2315,7 @@ 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; -@@ -2187,6 +2323,7 @@ public class PurpurWorldConfig { - public boolean zoglinControllable = true; - public double zoglinMaxHealth = 40.0D; - public double zoglinScale = 1.0D; -+ public boolean zoglinTakeDamageFromWater = false; - private void zoglinSettings() { - zoglinRidable = getBoolean("mobs.zoglin.ridable", zoglinRidable); - zoglinRidableInWater = getBoolean("mobs.zoglin.ridable-in-water", zoglinRidableInWater); -@@ -2198,6 +2335,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean zombieRidable = false; -@@ -2211,6 +2349,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); -@@ -2228,6 +2367,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 zombieHorseRidable = false; -@@ -2240,6 +2380,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() { - zombieHorseRidable = getBoolean("mobs.zombie_horse.ridable", zombieHorseRidable); - zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater); -@@ -2257,6 +2398,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; -@@ -2268,6 +2410,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); -@@ -2283,6 +2426,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; -@@ -2295,6 +2439,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); -@@ -2311,5 +2456,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/0123-Config-to-always-tame-in-Creative.patch b/patches/server/0123-Config-to-always-tame-in-Creative.patch deleted file mode 100644 index df4fffd320..0000000000 --- a/patches/server/0123-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 b0944fa1f3849dd24cd010fa0a6638f5fd7179d1..d409ae987088df3d47192128401d7491aaabc87c 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 -@@ -67,7 +67,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 && entityhuman.hasInfiniteMaterials()) || (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(entityhuman); - 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 75a961906658ff271bc0acf849648ce877c9e1be..9bf07130a86ac8ab153eb7547c451d35eb2bb016 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -524,7 +524,7 @@ public class Cat extends TamableAnimal 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 931cb90a8b32eb47d5985807d74d8ef7f1d01baf..218263958a90dfd95e4ecb7218c9eeefb069e50d 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 -@@ -49,6 +49,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; -@@ -177,16 +193,18 @@ public class EndCrystal extends Entity { - } - // CraftBukkit end - 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()) { - return false; - } - - this.remove(Entity.RemovalReason.KILLED, EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause -- world.explode(this, damagesource1, (ExplosionDamageCalculator) null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.BLOCK); -+ world.explode(this, damagesource1, (ExplosionDamageCalculator) null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), getExplosionEffect()); // Purpur -+ } else this.unsetRemoved(); // Purpur - } else { - this.remove(Entity.RemovalReason.KILLED, EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 54ca30f1d621a2b5124f3ec090a55857e4705ddd..a1e83e77b5c844de175005b2772c8c47ee65634d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -425,6 +425,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/0125-Configs-for-if-Wither-Ender-Dragon-can-ride-vehicles.patch b/patches/server/0125-Configs-for-if-Wither-Ender-Dragon-can-ride-vehicles.patch deleted file mode 100644 index 72c65c631e..0000000000 --- a/patches/server/0125-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 587b477305addbd1e5c71f34dc9df07be7f60384..d3721fcd9a537ad1e3c5712cd78bd436ac3e1a8b 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 -@@ -1171,6 +1171,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 cb3277d101b48db434a7e3155bf54559d6c13ba2..7bdc2b79fbf58f248caf63008ce898b9a43bfcd3 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 -@@ -729,6 +729,7 @@ public class WitherBoss extends Monster implements 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 a1e83e77b5c844de175005b2772c8c47ee65634d..066a6407ed76a3a2b77541f24d4c3429d3a1a189 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -979,6 +979,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); -@@ -997,6 +998,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; -@@ -2283,6 +2285,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); -@@ -2303,6 +2306,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/0126-Dont-run-with-scissors.patch b/patches/server/0126-Dont-run-with-scissors.patch deleted file mode 100644 index 5e51127ca9..0000000000 --- a/patches/server/0126-Dont-run-with-scissors.patch +++ /dev/null @@ -1,181 +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! - -inspired by https://modrinth.com/mod/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 2e714a4faf07e7ca18fa073ff6660c5d4f377507..ea6713bd5a2807cf00d664296c13e076b2fca3a0 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1720,6 +1720,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - this.player.tryResetCurrentImpulseContext(); - } - -+ // Purpur start - Dont run with scissors! -+ if (this.player.serverLevel().purpurConfig.dontRunWithScissors && this.player.isSprinting() && !(this.player.serverLevel().purpurConfig.ignoreScissorsInWater && this.player.isInWater()) && !(this.player.serverLevel().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.serverLevel(), this.player.damageSources().scissors(), (float) this.player.serverLevel().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() - d3, this.player.getY() - d4, this.player.getZ() - d5); - this.lastGoodX = this.player.getX(); - this.lastGoodY = this.player.getY(); -@@ -1759,6 +1766,17 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - } - } - -+ // Purpur start - Dont run with scissors! -+ public boolean isScissors(ItemStack stack) { -+ if (!stack.is(Items.SHEARS)) return false; -+ -+ ResourceLocation itemModelReference = stack.get(net.minecraft.core.component.DataComponents.ITEM_MODEL); -+ if (itemModelReference != null && itemModelReference.equals(this.player.serverLevel().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! -+ - // Paper start - optimise out extra getCubes - private boolean hasNewCollision(final ServerLevel world, final Entity entity, final AABB oldBox, final AABB newBox) { - final List collisionsBB = new java.util.ArrayList<>(); -diff --git a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -index 99a7e9eb75231c15bd8bb24fbb4e296bc9fdedff..254aea4138afa1009a3c949a24c1a0fb8edbbd1d 100644 ---- a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -+++ b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -@@ -98,6 +98,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 - Dont run with scissors! -+ if (damageSource.isScissors()) { -+ return damageSource.getLocalizedDeathMessage(org.purpurmc.purpur.PurpurConfig.deathMsgRunWithScissors, this.mob); -+ } -+ // Purpur end - Dont run with scissors! - 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 ab0ba4406dcaa915435c3f53ac9ca06fb21c673b..43985b4442145728c28bbeb9d18444a7c8f3a41f 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -@@ -29,6 +29,7 @@ public class DamageSource { - private boolean sweep = false; - private boolean melting = false; - private boolean poison = false; -+ private boolean scissors = false; // Purpur - Dont run with scissors! - @Nullable - private Entity customEventDamager = null; // This field is a helper for when causing entity damage is not set by vanilla // Paper - fix DamageSource API - -@@ -59,6 +60,17 @@ public class DamageSource { - return this.poison; - } - -+ // Purpur start - Dont run with scissors! -+ public DamageSource scissors() { -+ this.scissors = true; -+ return this; -+ } -+ -+ public boolean isScissors() { -+ return this.scissors; -+ } -+ // Purpur end - Dont run with scissors! -+ - // Paper start - fix DamageSource API - @Nullable - public Entity getCustomEventDamager() { -@@ -117,6 +129,7 @@ public class DamageSource { - damageSource.sweep = this.isSweep(); - damageSource.poison = this.isPoison(); - damageSource.melting = this.isMelting(); -+ damageSource.scissors = this.isScissors(); // Purpur - Dont run with scissors! - return damageSource; - } - // CraftBukkit end -diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSources.java b/src/main/java/net/minecraft/world/damagesource/DamageSources.java -index be87cb3cfa15a7d889118cdc4b87232e30749023..a4f710ad1aecd8265cde8d71d55eea952cd3c03b 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSources.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSources.java -@@ -46,11 +46,13 @@ public class DamageSources { - // CraftBukkit start - private final DamageSource melting; - private final DamageSource poison; -+ private final DamageSource scissors; // Purpur - Dont run with scissors! - - public DamageSources(RegistryAccess registryManager) { - this.damageTypes = registryManager.lookupOrThrow(Registries.DAMAGE_TYPE); - this.melting = this.source(DamageTypes.ON_FIRE).melting(); - this.poison = this.source(DamageTypes.MAGIC).poison(); -+ this.scissors = this.source(DamageTypes.MAGIC).scissors(); // Purpur - Dont run with scissors! - // CraftBukkit end - this.inFire = this.source(DamageTypes.IN_FIRE); - this.campfire = this.source(DamageTypes.CAMPFIRE); -@@ -101,6 +103,12 @@ public class DamageSources { - } - // CraftBukkit end - -+ // Purpur start - Dont run with scissors! -+ public DamageSource scissors() { -+ return this.scissors; -+ } -+ // Purpur end - Dont run with scissors! -+ - public DamageSource inFire() { - return this.inFire; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 6c7b7d6c6aff3dfd4bcc307c004513cf23f44a4d..693b9f04a35f055ea21a1b6be5ac27a04f1d80ad 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -186,6 +186,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); -@@ -197,6 +198,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 = io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 066a6407ed76a3a2b77541f24d4c3429d3a1a189..2fd0df56e94dca5ce91d0db29b56fa4bd5a2d1f2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -191,6 +191,11 @@ public class PurpurWorldConfig { - public List itemImmuneToExplosion = new ArrayList<>(); - public List itemImmuneToFire = new ArrayList<>(); - public List itemImmuneToLightning = new ArrayList<>(); -+ public boolean dontRunWithScissors = false; -+ public ResourceLocation dontRunWithScissorsItemModelReference = ResourceLocation.parse("purpurmc:scissors"); -+ 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 -> { -@@ -228,6 +233,11 @@ public class PurpurWorldConfig { - Item item = BuiltInRegistries.ITEM.getValue(ResourceLocation.parse(key.toString())); - if (item != Items.AIR) itemImmuneToLightning.add(item); - }); -+ dontRunWithScissors = getBoolean("gameplay-mechanics.item.shears.damage-if-sprinting", dontRunWithScissors); -+ dontRunWithScissorsItemModelReference = ResourceLocation.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); - } - - public double minecartMaxSpeed = 0.4D; diff --git a/patches/server/0127-One-Punch-Man.patch b/patches/server/0127-One-Punch-Man.patch deleted file mode 100644 index fe9865fe38..0000000000 --- a/patches/server/0127-One-Punch-Man.patch +++ /dev/null @@ -1,56 +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! - -inspired by https://modrinth.com/mod/creative-one-punch - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index c513e6de7d89992720fc4139969377c261571b72..7b60c84df74cb542e2da3c8d81383cc4afc47a55 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1455,6 +1455,24 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.stopSleeping(); - } - -+ // Purpur start -+ if (source.getEntity() instanceof net.minecraft.world.entity.player.Player player && source.getEntity().level().purpurConfig.creativeOnePunch && !source.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 -+ - this.noActionTime = 0; - if (amount < 0.0F) { - amount = 0.0F; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 2fd0df56e94dca5ce91d0db29b56fa4bd5a2d1f2..e6c9b43eea433abb1a01d4bf591541b754beeebf 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -340,6 +340,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); -@@ -356,6 +357,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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0128-Configurable-Ender-Pearl-cooldown-damage-and-Endermi.patch b/patches/server/0128-Configurable-Ender-Pearl-cooldown-damage-and-Endermi.patch deleted file mode 100644 index 99e4ab7962..0000000000 --- a/patches/server/0128-Configurable-Ender-Pearl-cooldown-damage-and-Endermi.patch +++ /dev/null @@ -1,69 +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 bd2684528157f928460f2143dd71a48e11983123..a044a5242ce1d1f6e34ecd5b4568553428d2f0b8 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -@@ -152,7 +152,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { - return; - } - // CraftBukkit end -- if (this.random.nextFloat() < 0.05F && worldserver.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { -+ if (this.random.nextFloat() < worldserver.purpurConfig.enderPearlEndermiteChance && worldserver.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { // Purpur - Endermite entityendermite = (Endermite) EntityType.ENDERMITE.create(worldserver, EntitySpawnReason.TRIGGERED); - - if (entityendermite != null) { -@@ -170,7 +170,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { - if (entityplayer1 != null) { - entityplayer1.resetFallDistance(); - entityplayer1.resetCurrentImpulseContext(); -- entityplayer1.hurtServer(entityplayer.serverLevel(), this.damageSources().enderPearl().customEventDamager(this), 5.0F); // CraftBukkit // Paper - fix DamageSource API -+ entityplayer1.hurtServer(entityplayer.serverLevel(), this.damageSources().enderPearl().customEventDamager(this), this.level().purpurConfig.enderPearlDamage); // CraftBukkit // Paper - fix DamageSource API // Purpur - } - - this.playSound(worldserver, vec3d); -diff --git a/src/main/java/net/minecraft/world/item/EnderpearlItem.java b/src/main/java/net/minecraft/world/item/EnderpearlItem.java -index eaee34054233c8f0296b65a09f1287ba515496f2..83bd9b1eff5b7f581c3f0af6f0f15bdf4b9de201 100644 ---- a/src/main/java/net/minecraft/world/item/EnderpearlItem.java -+++ b/src/main/java/net/minecraft/world/item/EnderpearlItem.java -@@ -37,6 +37,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 / (world.getRandom().nextFloat() * 0.4F + 0.8F)); - user.awardStat(Stats.ITEM_USED.get(this)); -+ user.getCooldowns().addCooldown(itemstack, user.getAbilities().instabuild ? world.purpurConfig.enderPearlCooldownCreative : world.purpurConfig.enderPearlCooldown); // Purpur - } else { - // Paper end - PlayerLaunchProjectileEvent - 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 e6c9b43eea433abb1a01d4bf591541b754beeebf..e196d391b4c1a02211f221ef10dda9df05c5ab5c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -196,6 +196,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 -> { -@@ -238,6 +242,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/0129-Config-to-ignore-nearby-mobs-when-sleeping.patch b/patches/server/0129-Config-to-ignore-nearby-mobs-when-sleeping.patch deleted file mode 100644 index f873300cae..0000000000 --- a/patches/server/0129-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 92c0a7609e45a8cdb809ad7579f1713fc15675cf..6467e72fa43469ae065462e1a0720130dee336b3 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1789,7 +1789,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - return entitymonster.isPreventingPlayerRest(this.serverLevel(), this); - }); - -- if (!list.isEmpty()) { -+ if (!this.level().purpurConfig.playerSleepNearMonsters && !list.isEmpty()) { // Purpur - return Either.left(net.minecraft.world.entity.player.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 e196d391b4c1a02211f221ef10dda9df05c5ab5c..aa089685179635e74cc4033b5335c1e79fab08a2 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 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); -@@ -366,6 +367,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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0130-Add-back-player-spawned-endermite-API.patch b/patches/server/0130-Add-back-player-spawned-endermite-API.patch deleted file mode 100644 index 57fef7b301..0000000000 --- a/patches/server/0130-Add-back-player-spawned-endermite-API.patch +++ /dev/null @@ -1,80 +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 fa4f25c7aece94a7468da1d7ed3cbd1abbce9640..e48ebd0424828624c980e0c3e09a0cb37685be5e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Endermite.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Endermite.java -@@ -32,6 +32,7 @@ public class Endermite extends Monster { - - private static final int MAX_LIFE = 2400; - public int life; -+ private boolean isPlayerSpawned; // Purpur - Add back player spawned endermite API - - public Endermite(EntityType type, Level world) { - super(type, world); -@@ -68,6 +69,15 @@ public class Endermite extends Monster { - return this.level().purpurConfig.endermiteTakeDamageFromWater; - } - // Purpur end - Toggle for water sensitive mob damage -+ // 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)); -@@ -115,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 - Add back player spawned endermite API - } - - @Override - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); - nbt.putInt("Lifetime", this.life); -+ nbt.putBoolean("PlayerSpawned", this.isPlayerSpawned); // Purpur - Add back player spawned endermite API - } - - @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 a044a5242ce1d1f6e34ecd5b4568553428d2f0b8..0720df603b4f89dd6aa346091b13033ad5d62907 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java -@@ -156,6 +156,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile { - Endermite entityendermite = (Endermite) EntityType.ENDERMITE.create(worldserver, EntitySpawnReason.TRIGGERED); - - if (entityendermite != null) { -+ entityendermite.setPlayerSpawned(true); // Purpur - entityendermite.moveTo(entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot()); - worldserver.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..3604d92c122b5c8be823098ce7b91e57e976589c 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 - Add back player spawned endermite API - } - - @Override - public void setPlayerSpawned(boolean playerSpawned) { -- // Nop -+ getHandle().setPlayerSpawned(playerSpawned); // Purpur - Add back player spawned endermite API - } - // Paper start - @Override diff --git a/patches/server/0131-Config-Enderman-aggressiveness-towards-Endermites.patch b/patches/server/0131-Config-Enderman-aggressiveness-towards-Endermites.patch deleted file mode 100644 index 7692383b52..0000000000 --- a/patches/server/0131-Config-Enderman-aggressiveness-towards-Endermites.patch +++ /dev/null @@ -1,51 +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 a92c30b5d3079ce58e00b9824ec3a60e6b41f104..09a49b598c35c8d51142aee7b4958bc344536cb3 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 - Ridables - 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, ignored) -> 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 aa089685179635e74cc4033b5335c1e79fab08a2..fe8988fc29be469af3d33fb87f0c88ee35f0b779 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1032,6 +1032,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); -@@ -1041,12 +1043,18 @@ 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); - 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); - 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/0132-Config-to-ignore-Dragon-Head-wearers-and-stare-aggro.patch b/patches/server/0132-Config-to-ignore-Dragon-Head-wearers-and-stare-aggro.patch deleted file mode 100644 index 0656e5abbb..0000000000 --- a/patches/server/0132-Config-to-ignore-Dragon-Head-wearers-and-stare-aggro.patch +++ /dev/null @@ -1,41 +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 09a49b598c35c8d51142aee7b4958bc344536cb3..efeb8a214c3e0ab6cb28a6f6e0d6417916ed670f 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -260,7 +260,7 @@ public class EnderMan extends Monster implements NeutralMob { - - boolean isBeingStaredBy(Player player) { - // Paper start - EndermanAttackPlayerEvent -- final boolean shouldAttack = isBeingStaredBy0(player); -+ final boolean shouldAttack = !this.level().purpurConfig.endermanDisableStareAggro && isBeingStaredBy0(player); // Purpur - 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(); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fe8988fc29be469af3d33fb87f0c88ee35f0b779..c1f1c20bfa038e66e3fc2dab1a2d5ccc1bbbd3f2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1034,6 +1034,7 @@ public class PurpurWorldConfig { - public boolean endermanTakeDamageFromWater = true; - public boolean endermanAggroEndermites = true; - public boolean endermanAggroEndermitesOnlyIfPlayerSpawned = false; -+ public boolean endermanDisableStareAggro = false; - private void endermanSettings() { - endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable); - endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater); -@@ -1055,6 +1056,7 @@ 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); -+ endermanDisableStareAggro = getBoolean("mobs.enderman.disable-player-stare-aggression", endermanDisableStareAggro); - } - - public boolean endermiteRidable = false; diff --git a/patches/server/0133-Tick-fluids-config.patch b/patches/server/0133-Tick-fluids-config.patch deleted file mode 100644 index 7ea964c575..0000000000 --- a/patches/server/0133-Tick-fluids-config.patch +++ /dev/null @@ -1,58 +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 a2d023ff011f71f80032f02430a53d6a08a23623..8c1c24e98f79888faee204129145746279d5d159 100644 ---- a/src/main/java/net/minecraft/world/level/block/LiquidBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/LiquidBlock.java -@@ -140,7 +140,7 @@ public class LiquidBlock extends Block implements BucketPickup { - - @Override - protected 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 - Tick fluids config - world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper - Configurable speed for water flowing over lava - } - -@@ -168,7 +168,7 @@ public class LiquidBlock extends Block implements BucketPickup { - - @Override - protected BlockState updateShape(BlockState state, LevelReader world, ScheduledTickAccess tickView, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random) { -- if (state.getFluidState().isSource() || neighborState.getFluidState().isSource()) { -+ if (world.getWorldBorder().world.purpurConfig.tickFluids && state.getFluidState().isSource() || neighborState.getFluidState().isSource()) { // Purpur - Tick fluids config - tickView.scheduleTick(pos, state.getFluidState().getType(), this.fluid.getTickDelay(world)); - } - -@@ -177,7 +177,7 @@ public class LiquidBlock extends Block implements BucketPickup { - - @Override - protected void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, @Nullable Orientation wireOrientation, boolean notify) { -- if (this.shouldSpreadLiquid(world, pos, state)) { -+ if (world.purpurConfig.tickFluids && this.shouldSpreadLiquid(world, pos, state)) { // Purpur - Tick fluids config - world.scheduleTick(pos, state.getFluidState().getType(), this.getFlowSpeed(world, pos)); // Paper - Configurable speed for water flowing over lava - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index c1f1c20bfa038e66e3fc2dab1a2d5ccc1bbbd3f2..dd33aa2d92296090d6d75d294e91f2272b57e6fc 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -135,6 +135,7 @@ public class PurpurWorldConfig { - public boolean imposeTeleportRestrictionsOnGateways = false; - public boolean imposeTeleportRestrictionsOnNetherPortals = false; - public boolean imposeTeleportRestrictionsOnEndPortals = false; -+ public boolean tickFluids = true; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative); -@@ -154,7 +155,7 @@ public class PurpurWorldConfig { - 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); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0134-Config-to-disable-Llama-caravans.patch b/patches/server/0134-Config-to-disable-Llama-caravans.patch deleted file mode 100644 index fd35a4bd76..0000000000 --- a/patches/server/0134-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 eb0faf58fa1a408f294fc62120b140def97f998d..0f4f546cd0eda4bd82b47446ae23ac32da8a9556 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.0, 4.0, 9.0), 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 fa1bf3e2e11564b9528e056369a610cb5bbd25d0..ee8d756fed5b6458b28acbd2a62a5693baddd45d 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 -@@ -464,7 +464,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 76e0dcedc72576c0e0a85e09a762aa5e2b63c7ef..58ef6c538a33199e5a8423223c22c61bf91fccae 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -65,6 +65,7 @@ public class Creeper extends Monster { - private int prevSpacebarCharge = 0; - private int powerToggleDelay = 0; - // Purpur end - Ridables -+ private boolean exploding = false; // Purpur - Config to make Creepers explode on death - - public Creeper(EntityType type, Level world) { - super(type, world); -@@ -274,6 +275,16 @@ public class Creeper extends Monster { - } - // Purpur end - Toggle for water sensitive mob damage - -+ // 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 - protected SoundEvent getHurtSound(DamageSource source) { - return SoundEvents.CREEPER_HURT; -@@ -362,6 +373,7 @@ public class Creeper extends Monster { - - public void explodeCreeper() { - Level world = this.level(); -+ this.exploding = true; // Purpur - Config to make Creepers explode on death - - if (world instanceof ServerLevel worldserver) { - float f = this.isPowered() ? 2.0F : 1.0F; -@@ -383,6 +395,7 @@ public class Creeper extends Monster { - // CraftBukkit end - } - -+ this.exploding = false; // Purpur - Config to make Creepers explode on death - } - - private void spawnLingeringCloud() { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index c62a22c8c7821041d890123c61cb04fb43955108..36262ccf9c5d308230ad112078d14a78f41243bd 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -877,6 +877,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); -@@ -892,6 +893,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/0136-Configurable-ravager-griefable-blocks-list.patch b/patches/server/0136-Configurable-ravager-griefable-blocks-list.patch deleted file mode 100644 index 511813df0c..0000000000 --- a/patches/server/0136-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 39081629685e3c7cd109626d1d61ce24b6afb860..7fb2af68c101dc12e60f120da1bb5c1efd20d164 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -198,7 +198,7 @@ public class Ravager extends Raider { - BlockState iblockdata = worldserver.getBlockState(blockposition); - Block block = iblockdata.getBlock(); - -- if (block instanceof LeavesBlock) { -+ if (this.level().purpurConfig.ravagerGriefableBlocks.contains(block)) { // Purpur - Configurable ravager griefable blocks list - // 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 e1cfad4834fdee910bf261a60e2b76678a0fec6d..00a06146e119a47eeaf66d240b8dd84e38498676 100644 ---- a/src/main/java/net/minecraft/world/level/block/CropBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java -@@ -180,7 +180,7 @@ public class CropBlock extends BushBlock implements BonemealableBlock { - protected 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 - Add EntityInsideBlockEvent - if (world instanceof ServerLevel worldserver) { -- if (entity instanceof Ravager && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !world.purpurConfig.ravagerBypassMobGriefing == !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Purpur - Add mobGriefing bypass to everything affected -+ if (entity instanceof Ravager && world.purpurConfig.ravagerGriefableBlocks.contains(world.getBlockState(pos).getBlock()) && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !world.purpurConfig.ravagerBypassMobGriefing == !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Purpur - Add mobGriefing bypass to everything affected // Purpur - Configurable ravager griefable blocks list - worldserver.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 36262ccf9c5d308230ad112078d14a78f41243bd..99a1c69938be124ad6fd1ae9a96024cc9fafd62c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1783,6 +1783,7 @@ public class PurpurWorldConfig { - public double ravagerScale = 1.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); -@@ -1796,6 +1797,23 @@ public class PurpurWorldConfig { - ravagerScale = Mth.clamp(getDouble("mobs.ravager.attributes.scale", ravagerScale), 0.0625D, 16.0D); - 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.getValue(ResourceLocation.parse(key.toString())); -+ if (!block.defaultBlockState().isAir()) { -+ ravagerGriefableBlocks.add(block); -+ } -+ }); - } - - public boolean salmonRidable = false; diff --git a/patches/server/0137-Sneak-to-bulk-process-composter.patch b/patches/server/0137-Sneak-to-bulk-process-composter.patch deleted file mode 100644 index 09ec5a441a..0000000000 --- a/patches/server/0137-Sneak-to-bulk-process-composter.patch +++ /dev/null @@ -1,104 +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 a96f859a5d0c6ec692d4627a69f3c9ee49199dbc..adecb4915a0428b71e8c1e59b11f94dd1776697d 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -583,7 +583,7 @@ public class ServerPlayerGameMode { - ItemStack itemstack1 = stack.copy(); - InteractionResult enuminteractionresult; - -- if (!flag1) { -+ if (!flag1 || (player.level().purpurConfig.composterBulkProcess && iblockdata.is(Blocks.COMPOSTER))) { // Purpur - InteractionResult enuminteractionresult1 = iblockdata.useItemOn(player.getItemInHand(hand), world, player, hand, hitResult); - - if (enuminteractionresult1.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 9264ba58188a7a682eeb8eb449b89ff8e60f91d6..809a820dd8eec3e48dd3263335c62fbea4cd4f2c 100644 ---- a/src/main/java/net/minecraft/world/level/block/ComposterBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/ComposterBlock.java -@@ -243,18 +243,27 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { - int i = (Integer) state.getValue(ComposterBlock.LEVEL); - - if (i < 8 && ComposterBlock.COMPOSTABLES.containsKey(stack.getItem())) { -- if (i < 7 && !world.isClientSide) { -- BlockState iblockdata1 = ComposterBlock.addItem(player, state, world, pos, stack); -- // Paper start - handle cancelled events -- if (iblockdata1 == null) { -- return InteractionResult.PASS; -- } -- // Paper end -- -- world.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0); -- player.awardStat(Stats.ITEM_USED.get(stack.getItem())); -- stack.consume(1, player); -+ // Purpur start - sneak to bulk process composter -+ BlockState newState = process(i, player, state, world, pos, stack); -+ if (newState == null) { -+ return InteractionResult.PASS; - } -+ if (world.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, world, 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 - - return InteractionResult.SUCCESS; - } else { -@@ -262,6 +271,25 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { - } - } - -+ // Purpur start - sneak to bulk process composter -+ private static @Nullable BlockState process(int level, Player player, BlockState state, Level world, BlockPos pos, ItemStack stack) { -+ if (level < 7 && !world.isClientSide) { -+ BlockState iblockdata1 = ComposterBlock.addItem(player, state, world, pos, stack); -+ // Paper start - handle cancelled events -+ if (iblockdata1 == null) { -+ return null; -+ } -+ // Paper end -+ -+ world.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0); -+ player.awardStat(Stats.ITEM_USED.get(stack.getItem())); -+ stack.consume(1, player); -+ return iblockdata1; -+ } -+ return state; -+ } -+ // Purpur end -+ - @Override - protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) { - 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 99a1c69938be124ad6fd1ae9a96024cc9fafd62c..09fa9a18dc1bb6a3df80cb2237dc0d55affc0453 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -441,6 +441,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/0138-Config-for-skipping-night.patch b/patches/server/0138-Config-for-skipping-night.patch deleted file mode 100644 index 699ddae81d..0000000000 --- a/patches/server/0138-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 6585e0d72b2fbb8af5c62ceba57eef2fb7794e87..56fe25076109827815d7d8ebc45a62d391462bbf 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -749,7 +749,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - 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 09fa9a18dc1bb6a3df80cb2237dc0d55affc0453..59156b6d2b8107a2031bc56387f002a6ef404022 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -351,6 +351,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); -@@ -369,6 +370,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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0139-Add-config-for-villager-trading.patch b/patches/server/0139-Add-config-for-villager-trading.patch deleted file mode 100644 index 112f4ee787..0000000000 --- a/patches/server/0139-Add-config-for-villager-trading.patch +++ /dev/null @@ -1,69 +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 e33a29c486d50c5f22888bcb1979830143c8b06d..0bca1eeaa0f35d702dad41758c72bb3c9ab35220 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -377,6 +377,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - } - - if (level().purpurConfig.villagerRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur - Ridables -+ if (this.level().purpurConfig.villagerAllowTrading) // Purpur - Add config for villager trading - 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 455390bd5350282d1a6c9b25e9cdbd4a620b136c..2cfd9c9194edda92185adecca80c5cd140e26c9f 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -165,8 +165,10 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - return tryRide(player, hand, InteractionResult.CONSUME); // Purpur - Ridables - } - if (level().purpurConfig.wanderingTraderRidable && itemstack.isEmpty()) return tryRide(player, hand); // Purpur - Ridables -+ 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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 59156b6d2b8107a2031bc56387f002a6ef404022..913c3e0c66d005380782af7e7a3358ed5ef45afc 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2238,6 +2238,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); -@@ -2258,6 +2259,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; -@@ -2291,6 +2293,7 @@ public class PurpurWorldConfig { - public double wanderingTraderTemptRange = 10.0D; - 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); -@@ -2306,6 +2309,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean wardenRidable = false; diff --git a/patches/server/0140-Drowning-Settings.patch b/patches/server/0140-Drowning-Settings.patch deleted file mode 100644 index dcbf8ab070..0000000000 --- a/patches/server/0140-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 f15a8c14417b83b237dc7b9a11490116314cd4c6..a5f4f5d915a864240fd738ca32872829bfcabb41 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -3692,7 +3692,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - - 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 7b60c84df74cb542e2da3c8d81383cc4afc47a55..dc0656378d81a77e1d49895a90f92f27e3e247f3 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -489,7 +489,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(); - -@@ -501,7 +501,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.level().addParticle(ParticleTypes.BUBBLE, this.getX() + d0, this.getY() + d2, this.getZ() + d3, vec3d.x, vec3d.y, vec3d.z); - } - -- this.hurt(this.damageSources().drown(), 2.0F); -+ this.hurt(this.damageSources().drown(), (float) this.level().purpurConfig.damageFromDrowning); // Purpur - } - } else if (this.getAirSupply() < this.getMaxAirSupply()) { - this.setAirSupply(this.increaseAirSupply(this.getAirSupply())); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 913c3e0c66d005380782af7e7a3358ed5ef45afc..693f84494c622697b4803f81ee81e21c59a7ebe6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -165,6 +165,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 int elytraDamagePerFireworkBoost = 0; diff --git a/patches/server/0141-Break-individual-slabs-when-sneaking.patch b/patches/server/0141-Break-individual-slabs-when-sneaking.patch deleted file mode 100644 index dcc00de184..0000000000 --- a/patches/server/0141-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 adecb4915a0428b71e8c1e59b11f94dd1776697d..7315e604a2f9cb068eb5bbca744e44eeabac09c9 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -403,6 +403,7 @@ public class ServerPlayerGameMode { - } else {capturedBlockEntity = true;} // Paper - Send block entities after destroy prediction - 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 9274fd639c22e305dda567b303f9b01068adb52c..4433e432ea0ee8d11045b87e68dac3ed43e8cf82 100644 ---- a/src/main/java/net/minecraft/world/level/block/SlabBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SlabBlock.java -@@ -150,4 +150,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 693f84494c622697b4803f81ee81e21c59a7ebe6..60413ce72af16a81e97b875da85988fec8c88780 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -562,6 +562,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/0142-Config-to-disable-hostile-mob-spawn-on-ice.patch b/patches/server/0142-Config-to-disable-hostile-mob-spawn-on-ice.patch deleted file mode 100644 index 580511620b..0000000000 --- a/patches/server/0142-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 e2de074bbe7bab0e5a7aecc1fae4c5914a203dd4..c2061f575c731ecc6071384b007517c08e0cf983 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Monster.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Monster.java -@@ -88,6 +88,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 60413ce72af16a81e97b875da85988fec8c88780..f83e25e7586aa5b84a6a87cdd32ca7ee005e01c6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -527,6 +527,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/0143-Config-to-show-Armor-Stand-arms-on-spawn.patch b/patches/server/0143-Config-to-show-Armor-Stand-arms-on-spawn.patch deleted file mode 100644 index a62f1e0fde..0000000000 --- a/patches/server/0143-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 e1c7a644dc922ca726cebf71ec2f0b159d6db289..572bb7e1f6ae75f6dfe53b0e100b3654e42bf4c2 100644 ---- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java -@@ -128,6 +128,7 @@ public class ArmorStand extends LivingEntity { - this.rightArmPose = ArmorStand.DEFAULT_RIGHT_ARM_POSE; - this.leftLegPose = ArmorStand.DEFAULT_LEFT_LEG_POSE; - this.rightLegPose = ArmorStand.DEFAULT_RIGHT_LEG_POSE; -+ 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 f83e25e7586aa5b84a6a87cdd32ca7ee005e01c6..0c3438c1914d6ec828c270fecfc7cf101fcfd211 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -103,6 +103,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); -@@ -110,6 +111,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/0144-Option-to-make-doors-require-redstone.patch b/patches/server/0144-Option-to-make-doors-require-redstone.patch deleted file mode 100644 index 12b9ef121d..0000000000 --- a/patches/server/0144-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 3513b15f6622bfc134ecfcd9129f81a8acc2c601..2768e2a61ab7fbd82c2b8787e715163a7b0450b9 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.MOB_INTERACTABLE_DOORS, (blockbase_blockdata) -> { - return blockbase_blockdata.getBlock() instanceof DoorBlock; -- })) { -+ }) && !DoorBlock.requiresRedstone(entityliving.level(), iblockdata, blockposition)) { // Purpur - Option to make doors require redstone - DoorBlock blockdoor = (DoorBlock) iblockdata.getBlock(); - - if (!blockdoor.isOpen(iblockdata)) { -@@ -79,7 +79,7 @@ public class InteractWithDoor { - - if (iblockdata1.is(BlockTags.MOB_INTERACTABLE_DOORS, (blockbase_blockdata) -> { - return blockbase_blockdata.getBlock() instanceof DoorBlock; -- })) { -+ }) && !DoorBlock.requiresRedstone(entityliving.level(), iblockdata, blockposition1)) { // Purpur - Option to make doors require redstone - DoorBlock blockdoor1 = (DoorBlock) iblockdata1.getBlock(); - - if (!blockdoor1.isOpen(iblockdata1)) { -@@ -122,7 +122,7 @@ public class InteractWithDoor { - - if (!iblockdata.is(BlockTags.MOB_INTERACTABLE_DOORS, (blockbase_blockdata) -> { - return blockbase_blockdata.getBlock() instanceof DoorBlock; -- })) { -+ }) || DoorBlock.requiresRedstone(entity.level(), iblockdata, blockposition)) { // Purpur - Option to make doors require redstone - 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 077b99caf0ec0ee098786d23194d88e1dc4481ce..f8356e468841137dcc92b2fe5db1cafa24619eaf 100644 ---- a/src/main/java/net/minecraft/world/level/block/DoorBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/DoorBlock.java -@@ -200,6 +200,7 @@ public class DoorBlock extends Block { - protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) { - if (!this.type.canOpenByHand()) { - return InteractionResult.PASS; -+ } else if (requiresRedstone(world, state, pos)) { return InteractionResult.CONSUME; // Purpur - Option to make doors require redstone - } else { - state = (BlockState) state.cycle(DoorBlock.OPEN); - world.setBlock(pos, state, 10); -@@ -301,4 +302,18 @@ public class DoorBlock extends Block { - flag = false; - return flag; - } -+ -+ // 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, 3); -+ level.sendBlockUpdated(otherPos, otherState, otherState, 3); -+ return true; -+ } -+ return false; -+ } -+ // Purpur end - Option to make doors require redstone - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0c3438c1914d6ec828c270fecfc7cf101fcfd211..88f2cf0023540a2fed5be6f7929e46dc7144673c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -466,6 +466,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.getValue(ResourceLocation.parse(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/0145-Config-to-allow-unsafe-enchants.patch b/patches/server/0145-Config-to-allow-unsafe-enchants.patch deleted file mode 100644 index 70cff05756..0000000000 --- a/patches/server/0145-Config-to-allow-unsafe-enchants.patch +++ /dev/null @@ -1,137 +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 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 cf0a5943f457c532958f40b4989fa18f967abae6..2ab8ff8ca51eb841932ccca4a348acc0141264a8 100644 ---- a/src/main/java/net/minecraft/server/commands/EnchantCommand.java -+++ b/src/main/java/net/minecraft/server/commands/EnchantCommand.java -@@ -70,7 +70,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 - Config to allow unsafe enchants - throw ERROR_LEVEL_TOO_HIGH.create(level, enchantment2.getMaxLevel()); - } else { - int i = 0; -@@ -81,7 +81,7 @@ public class EnchantCommand { - ItemStack itemStack = livingEntity.getMainHandItem(); - if (!itemStack.isEmpty()) { - if (enchantment2.canEnchant(itemStack) -- && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantmentsForCrafting(itemStack).keySet(), enchantment)) { -+ && EnchantmentHelper.isEnchantmentCompatible(EnchantmentHelper.getEnchantmentsForCrafting(itemStack).keySet(), enchantment) || (org.purpurmc.purpur.PurpurConfig.allowUnsafeEnchantCommand && !itemStack.hasEnchantment(enchantment))) { // Purpur - Config to allow unsafe enchants - itemStack.enchant(enchantment, 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 a7c3338ed6edcd26fce869ec66fdcaa4c32c0e4f..346681cf9c3c9ff5274f63236b0cc7c4eec76492 100644 ---- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -@@ -234,7 +234,10 @@ public class AnvilMenu extends ItemCombinerMenu { - - i2 = l1 == i2 ? i2 + 1 : Math.max(i2, l1); - Enchantment enchantment = (Enchantment) holder.value(); -- boolean flag3 = enchantment.canEnchant(itemstack); -+ // Purpur start - Config to allow unsafe enchants -+ boolean flag3 = this.canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowInapplicableEnchants || enchantment.canEnchant(itemstack); // whether the enchantment can be applied on specific item type -+ boolean flag4 = true; // whether two incompatible enchantments can be applied on a single item -+ // Purpur end - Config to allow unsafe enchants - - if (this.player.getAbilities().instabuild || itemstack.is(Items.ENCHANTED_BOOK)) { - flag3 = true; -@@ -246,16 +249,22 @@ public class AnvilMenu extends ItemCombinerMenu { - Holder holder1 = (Holder) iterator1.next(); - - if (!holder1.equals(holder) && !Enchantment.areCompatible(holder, holder1)) { -- flag3 = this.canDoUnsafeEnchants; // Purpur - Anvil API -+ flag4 = this.canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowIncompatibleEnchants; // Purpur - Anvil API // Purpur - flag3 -> flag4 - Config to allow unsafe enchants -+ // Purpur start - Config to allow unsafe enchants -+ if (!flag4 && org.purpurmc.purpur.PurpurConfig.replaceIncompatibleEnchants) { -+ iterator1.remove(); // replace current enchant with the incompatible one trying to be applied -+ flag4 = true; -+ } -+ // Purpur end - Config to allow unsafe enchants - ++i; - } - } - -- if (!flag3) { -+ if (!flag3 || !flag4) { // Purpur - Config to allow unsafe enchants - flag2 = true; - } else { - flag1 = true; -- if (i2 > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions -+ if (!org.purpurmc.purpur.PurpurConfig.allowHigherEnchantsLevels && i2 > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions // Purpur - Config to allow unsafe enchants - i2 = enchantment.getMaxLevel(); - } - -@@ -384,7 +393,7 @@ public class AnvilMenu extends ItemCombinerMenu { - this.broadcastChanges(); - - // Purpur start - Anvil API -- if (this.canDoUnsafeEnchants && itemstack1 != ItemStack.EMPTY) { -+ if ((this.canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowInapplicableEnchants || org.purpurmc.purpur.PurpurConfig.allowIncompatibleEnchants) && itemstack1 != ItemStack.EMPTY) { // Purpur - Config to allow unsafe enchants - ((ServerPlayer) this.player).connection.send(new ClientboundContainerSetSlotPacket(this.containerId, this.incrementStateId(), 2, itemstack1)); - ((ServerPlayer) this.player).connection.send(new ClientboundContainerSetDataPacket(this.containerId, 0, this.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 c616ebbf0f709b8c3a500a23a2190f7023f7757d..aac771d7d8aefc6d5fcb497763ab9bac1ce2dc31 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -1369,6 +1369,12 @@ public final class ItemStack implements DataComponentHolder { - return !((ItemEnchantments) 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 (ItemEnchantments) this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY); - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 693b9f04a35f055ea21a1b6be5ac27a04f1d80ad..eb9e18e2b71559ed945c40d489618ac3bf976e35 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -286,6 +286,36 @@ public class PurpurConfig { - cryingObsidianValidForPortalFrame = getBoolean("settings.blocks.crying_obsidian.valid-for-portal-frame", cryingObsidianValidForPortalFrame); - } - -+ 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; -+ 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); -+ } -+ - public static boolean endermanShortHeight = false; - private static void entitySettings() { - endermanShortHeight = getBoolean("settings.entity.enderman.short-height", endermanShortHeight); diff --git a/patches/server/0146-Configurable-sponge-absorption.patch b/patches/server/0146-Configurable-sponge-absorption.patch deleted file mode 100644 index abfb6fe570..0000000000 --- a/patches/server/0146-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 e9a77c1ae09af42d2d444ad6b5f6c8ac395044e1..0edb7e821a60fe95fea3cae900e5b88192946fe6 100644 ---- a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java -@@ -61,7 +61,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 - Configurable sponge absorption - 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 88f2cf0023540a2fed5be6f7929e46dc7144673c..1b1c6e20acef4e2dda93cbc89ae1d527d76e81ce 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -591,6 +591,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 = false; - public boolean turtleEggsBreakFromItems = false; - public boolean turtleEggsBreakFromMinecarts = false; diff --git a/patches/server/0147-Projectile-offset-config.patch b/patches/server/0147-Projectile-offset-config.patch deleted file mode 100644 index 2f1e5c60ba..0000000000 --- a/patches/server/0147-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 1d4d0799a86b9940b5e3b614c5a188ade5133f7e..58fa528e4b2589d362eb976afd6221cd94f2623c 100644 ---- a/src/main/java/net/minecraft/world/item/BowItem.java -+++ b/src/main/java/net/minecraft/world/item/BowItem.java -@@ -43,7 +43,7 @@ public class BowItem extends ProjectileWeaponItem { - } else { - List list = draw(stack, itemStack, player); - if (world instanceof ServerLevel serverLevel && !list.isEmpty()) { -- this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, f * 3.0F, 1.0F, f == 1.0F, null); -+ this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, f * 3.0F, (float) world.purpurConfig.bowProjectileOffset, f == 1.0F, null); // Purpur - } - - world.playSound( -diff --git a/src/main/java/net/minecraft/world/item/CrossbowItem.java b/src/main/java/net/minecraft/world/item/CrossbowItem.java -index be1902a307a54434644b242b429ad47c271d2a0c..cac4de9877b91bd805a5a8f4b84d27449fc5001f 100644 ---- a/src/main/java/net/minecraft/world/item/CrossbowItem.java -+++ b/src/main/java/net/minecraft/world/item/CrossbowItem.java -@@ -70,7 +70,7 @@ public class CrossbowItem extends ProjectileWeaponItem { - ItemStack itemStack = user.getItemInHand(hand); - ChargedProjectiles chargedProjectiles = itemStack.get(DataComponents.CHARGED_PROJECTILES); - if (chargedProjectiles != null && !chargedProjectiles.isEmpty()) { -- this.performShooting(world, user, hand, itemStack, getShootingPower(chargedProjectiles), 1.0F, null); -+ this.performShooting(world, user, hand, itemStack, getShootingPower(chargedProjectiles), (float) world.purpurConfig.crossbowProjectileOffset, null); // Purpur - return InteractionResult.CONSUME; - } else if (!user.getProjectile(itemStack).isEmpty()) { - this.startSoundPlayed = false; -diff --git a/src/main/java/net/minecraft/world/item/EggItem.java b/src/main/java/net/minecraft/world/item/EggItem.java -index 6d559fef484036194e4d899b82aaa7b5d518311e..9fb04b4e5b61ea497238e042fefa9a06f7489618 100644 ---- a/src/main/java/net/minecraft/world/item/EggItem.java -+++ b/src/main/java/net/minecraft/world/item/EggItem.java -@@ -29,7 +29,7 @@ public class EggItem extends Item implements ProjectileItem { - if (world instanceof ServerLevel worldserver) { - // CraftBukkit start - // Paper start - PlayerLaunchProjectileEvent -- final Projectile.Delayed thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, worldserver, itemstack, user, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, 1.0F); -+ final Projectile.Delayed thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, worldserver, itemstack, user, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, (float) worldserver.purpurConfig.eggProjectileOffset); // Purpur - 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) thrownEgg.projectile().getBukkitEntity()); - if (event.callEvent() && thrownEgg.attemptSpawn()) { - if (event.shouldConsume()) { -diff --git a/src/main/java/net/minecraft/world/item/EnderpearlItem.java b/src/main/java/net/minecraft/world/item/EnderpearlItem.java -index 83bd9b1eff5b7f581c3f0af6f0f15bdf4b9de201..392f2600e4fb1ff937c3ec5635156b358eb36888 100644 ---- a/src/main/java/net/minecraft/world/item/EnderpearlItem.java -+++ b/src/main/java/net/minecraft/world/item/EnderpearlItem.java -@@ -26,7 +26,7 @@ public class EnderpearlItem extends Item { - if (world instanceof ServerLevel worldserver) { - // CraftBukkit start - // Paper start - PlayerLaunchProjectileEvent -- final Projectile.Delayed thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, worldserver, itemstack, user, 0.0F, EnderpearlItem.PROJECTILE_SHOOT_POWER, 1.0F); -+ final Projectile.Delayed thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, worldserver, itemstack, user, 0.0F, EnderpearlItem.PROJECTILE_SHOOT_POWER, (float) worldserver.purpurConfig.enderPearlProjectileOffset); // Purpur - 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) thrownEnderpearl.projectile().getBukkitEntity()); - if (event.callEvent() && thrownEnderpearl.attemptSpawn()) { - if (event.shouldConsume()) { -diff --git a/src/main/java/net/minecraft/world/item/SnowballItem.java b/src/main/java/net/minecraft/world/item/SnowballItem.java -index d3bba4665ae14cc279c0f937831f909f8831b12b..27499f01ef0bc89c4c3f60eb696ca07cc5984809 100644 ---- a/src/main/java/net/minecraft/world/item/SnowballItem.java -+++ b/src/main/java/net/minecraft/world/item/SnowballItem.java -@@ -29,7 +29,7 @@ public class SnowballItem extends Item implements ProjectileItem { - // world.playSound((EntityHuman) null, entityhuman.getX(), entityhuman.getY(), entityhuman.getZ(), SoundEffects.SNOWBALL_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F)); - if (world instanceof ServerLevel worldserver) { - // Paper start - PlayerLaunchProjectileEvent -- final Projectile.Delayed snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, worldserver, itemstack, user, 0.0F, SnowballItem.PROJECTILE_SHOOT_POWER, 1.0F); -+ final Projectile.Delayed snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, worldserver, itemstack, user, 0.0F, SnowballItem.PROJECTILE_SHOOT_POWER, (float) worldserver.purpurConfig.snowballProjectileOffset); // Purpur - 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) snowball.projectile().getBukkitEntity()); - if (event.callEvent() && snowball.attemptSpawn()) { - user.awardStat(Stats.ITEM_USED.get(this)); -diff --git a/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java b/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java -index e422881d1ab0f1a5bb2cb741d23089a2e35de2d4..bbd65d35d91d4f3ffabeb355b82f22ddde0f868b 100644 ---- a/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java -+++ b/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java -@@ -23,7 +23,7 @@ public class ThrowablePotionItem extends PotionItem implements ProjectileItem { - ItemStack itemStack = user.getItemInHand(hand); - if (world instanceof ServerLevel serverLevel) { - // Paper start - PlayerLaunchProjectileEvent -- final Projectile.Delayed thrownPotion = Projectile.spawnProjectileFromRotationDelayed(ThrownPotion::new, serverLevel, itemStack, user, -20.0F, PROJECTILE_SHOOT_POWER, 1.0F); -+ final Projectile.Delayed thrownPotion = Projectile.spawnProjectileFromRotationDelayed(ThrownPotion::new, serverLevel, itemStack, user, -20.0F, PROJECTILE_SHOOT_POWER, (float) serverLevel.purpurConfig.throwablePotionProjectileOffset); // Purpur - // Paper start - PlayerLaunchProjectileEvent - 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.projectile().getBukkitEntity()); - if (event.callEvent() && thrownPotion.attemptSpawn()) { -diff --git a/src/main/java/net/minecraft/world/item/TridentItem.java b/src/main/java/net/minecraft/world/item/TridentItem.java -index aff4f33be216f62d6c6e139dcd7fd82efdbd267c..810082126567eb02bec395065b95b3c3902d4973 100644 ---- a/src/main/java/net/minecraft/world/item/TridentItem.java -+++ b/src/main/java/net/minecraft/world/item/TridentItem.java -@@ -89,7 +89,7 @@ public class TridentItem extends Item implements ProjectileItem { - // itemstack.hurtWithoutBreaking(1, entityhuman); // CraftBukkit - moved down - if (f == 0.0F) { - // Paper start - PlayerLaunchProjectileEvent -- Projectile.Delayed tridentDelayed = Projectile.spawnProjectileFromRotationDelayed(ThrownTrident::new, worldserver, stack, entityhuman, 0.0F, 2.5F, 1.0F); -+ Projectile.Delayed tridentDelayed = Projectile.spawnProjectileFromRotationDelayed(ThrownTrident::new, worldserver, stack, entityhuman, 0.0F, 2.5F, (float) worldserver.purpurConfig.tridentProjectileOffset); // Purpur - // Paper start - PlayerLaunchProjectileEvent - 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) tridentDelayed.projectile().getBukkitEntity()); - if (!event.callEvent() || !tridentDelayed.attemptSpawn()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1b1c6e20acef4e2dda93cbc89ae1d527d76e81ce..c6d39361fe5f02de3119e2d2bfc603e096c03160 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -416,6 +416,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/0148-Config-for-powered-rail-activation-distance.patch b/patches/server/0148-Config-for-powered-rail-activation-distance.patch deleted file mode 100644 index c5d21071cb..0000000000 --- a/patches/server/0148-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 b763361a8f0f1b46093d5dd9afe8dba0cadf9c78..bd14c08defe8afc5ceca59d16a5b1dbad178f594 100644 ---- a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java -@@ -30,7 +30,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 c6d39361fe5f02de3119e2d2bfc603e096c03160..e85eb5d3dbf35c89295302db5431a7b3cb2d3ba1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -577,6 +577,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/0149-Piglin-portal-spawn-modifier.patch b/patches/server/0149-Piglin-portal-spawn-modifier.patch deleted file mode 100644 index c7b19d3587..0000000000 --- a/patches/server/0149-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 5169cba4c43d80ce3597c57bf7d40bd0148ec8a0..2d53c57c961fa8977e37931775863665381595eb 100644 ---- a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -@@ -78,7 +78,7 @@ public class NetherPortalBlock extends Block implements Portal { - - @Override - protected 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 e85eb5d3dbf35c89295302db5431a7b3cb2d3ba1..be247fe9ca24407350044b020db613ce9a70ff26 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 double piglinScale = 1.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); -@@ -1728,6 +1729,7 @@ public class PurpurWorldConfig { - piglinScale = Mth.clamp(getDouble("mobs.piglin.attributes.scale", piglinScale), 0.0625D, 16.0D); - 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/0150-Config-to-change-max-number-of-bees.patch b/patches/server/0150-Config-to-change-max-number-of-bees.patch deleted file mode 100644 index 5dbcc264e0..0000000000 --- a/patches/server/0150-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 65a85b4a4e159cfe55e435ed342a87bcc07b21d5..7f0e37e23ff4c64355fdc822c0ac683959b8588a 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 -@@ -60,7 +60,7 @@ public class BeehiveBlockEntity extends BlockEntity { - private 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 eb9e18e2b71559ed945c40d489618ac3bf976e35..3310fb33efdf129c424503eb07055ddb4c3a200b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -253,6 +253,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); -@@ -284,6 +285,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 allowInapplicableEnchants = false; diff --git a/patches/server/0151-Config-for-wither-explosion-radius.patch b/patches/server/0151-Config-for-wither-explosion-radius.patch deleted file mode 100644 index dd386114f1..0000000000 --- a/patches/server/0151-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 45e0b9b3653de2dcb51a579f939b991beac03149..eeb91f7e744d20c1a05212308a23102a347b9c19 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java -@@ -103,7 +103,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 be247fe9ca24407350044b020db613ce9a70ff26..176dc97524c6383a4c10ebd0918fd2ca209af3b0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2416,6 +2416,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); -@@ -2437,6 +2438,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/0152-Gamemode-extra-permissions.patch b/patches/server/0152-Gamemode-extra-permissions.patch deleted file mode 100644 index e97cffe625..0000000000 --- a/patches/server/0152-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 7b2daf47e411362a462019a1612a99c952170200..8ed5e9293e80f53d741c145fa415fab6311036d7 100644 ---- a/src/main/java/net/minecraft/commands/CommandSourceStack.java -+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java -@@ -211,6 +211,19 @@ public class CommandSourceStack implements ExecutionCommandSource").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 d1da3600dc07107309b20ebe6e7c0c4da0e8de76..244b4719c689f153fa36381a60acc280bb0bd9b3 100644 ---- a/src/main/java/net/minecraft/server/commands/GameModeCommand.java -+++ b/src/main/java/net/minecraft/server/commands/GameModeCommand.java -@@ -57,6 +57,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 b3169c551b8410f5861f9db0543c785439ecba7c..916ca3f1f39e10158fc7c10141785fff49ed1501 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 3310fb33efdf129c424503eb07055ddb4c3a200b..7fda69055383d8b8740752999f0d1f9993c454e4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -236,6 +236,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())); -@@ -247,6 +248,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/0153-Configurable-piston-push-limit.patch b/patches/server/0153-Configurable-piston-push-limit.patch deleted file mode 100644 index f7a1f8a9f5..0000000000 --- a/patches/server/0153-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 205e223c356634bd6bc6bd58c6f0b7fda61a6f5f..bea05cb928d540a2f19b51bb7352d032b2dd69cd 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 -@@ -81,7 +81,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)) { -@@ -95,7 +95,7 @@ public class PistonStructureResolver { - break; - } - -- if (++i + this.toPush.size() > 12) { -+ if (++i + this.toPush.size() > this.level.purpurConfig.pistonBlockPushLimit) { // Purpur - return false; - } - } -@@ -140,7 +140,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 176dc97524c6383a4c10ebd0918fd2ca209af3b0..19e77eadb5ece2da43eb82f7b7d196e1616348af 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -572,6 +572,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/0154-Configurable-broadcast-settings.patch b/patches/server/0154-Configurable-broadcast-settings.patch deleted file mode 100644 index 75df7a9bab..0000000000 --- a/patches/server/0154-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 0a16aa193ef24aa8f1716f9e089b8027fa3c0a3c..1e85c9318ede93b8e9fe548a8945324b5b00e818 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.value().display().ifPresent((advancementdisplay) -> { - // Paper start - Add Adventure message to PlayerAdvancementDoneEvent - if (event.message() != null && this.player.serverLevel().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(event.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 6467e72fa43469ae065462e1a0720130dee336b3..4c69fa830a216c189f97ddfb62f0008b891455da 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1339,6 +1339,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - })); - PlayerTeam scoreboardteam = this.getTeam(); - -+ if (org.purpurmc.purpur.PurpurConfig.deathMessageOnlyBroadcastToAffectedPlayer) this.sendSystemMessage(ichatbasecomponent); else // Purpur - if (scoreboardteam != null && scoreboardteam.getDeathMessageVisibility() != Team.Visibility.ALWAYS) { - if (scoreboardteam.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 7fda69055383d8b8740752999f0d1f9993c454e4..3a0ed610320f9730806d463647f53857265aa2e8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -206,6 +206,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 = io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); - private static void serverModName() { - serverModName = getString("settings.server-mod-name", serverModName); diff --git a/patches/server/0155-Configurable-mob-blindness.patch b/patches/server/0155-Configurable-mob-blindness.patch deleted file mode 100644 index 6de679507e..0000000000 --- a/patches/server/0155-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 dc0656378d81a77e1d49895a90f92f27e3e247f3..28d3b200ab0c3e8993dfb89a3e48146898c6b85e 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1065,6 +1065,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 19e77eadb5ece2da43eb82f7b7d196e1616348af..1ae95a30b6e4cf69028dc61c398651bde526418f 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 boolean imposeTeleportRestrictionsOnNetherPortals = false; - public boolean imposeTeleportRestrictionsOnEndPortals = false; - public boolean tickFluids = true; -+ public double mobsBlindnessMultiplier = 1; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative); -@@ -158,6 +159,7 @@ public class PurpurWorldConfig { - 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); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0156-Hide-hidden-players-from-entity-selector.patch b/patches/server/0156-Hide-hidden-players-from-entity-selector.patch deleted file mode 100644 index 046fbbb50d..0000000000 --- a/patches/server/0156-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 c8d39e6e1c570c9219f6066da273dc0130920519..d881caf99c2bad66b76bdc6ddb11a6cac1e94db6 100644 ---- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java -+++ b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java -@@ -198,10 +198,10 @@ public class EntitySelector { - - if (this.playerName != null) { - entityplayer = source.getServer().getPlayerList().getPlayerByName(this.playerName); -- return entityplayer == null ? List.of() : List.of(entityplayer); -+ return entityplayer == null || !canSee(source, entityplayer) ? List.of() : List.of(entityplayer); // Purpur - Hide hidden players from entity selector - } else if (this.entityUUID != null) { - entityplayer = source.getServer().getPlayerList().getPlayer(this.entityUUID); -- return entityplayer == null ? List.of() : List.of(entityplayer); -+ return entityplayer == null || !canSee(source, entityplayer) ? List.of() : List.of(entityplayer); // Purpur - Hide hidden players from entity selector - } else { - Vec3 vec3d = (Vec3) this.position.apply(source.getPosition()); - AABB axisalignedbb = this.getAbsoluteAabb(vec3d); -@@ -214,7 +214,7 @@ public class EntitySelector { - ServerPlayer entityplayer1 = (ServerPlayer) entity; - - if (predicate.test(entityplayer1)) { -- return List.of(entityplayer1); -+ return !canSee(source, entityplayer1) ? List.of() : List.of(entityplayer1); // Purpur - Hide hidden players from entity selector - } - } - -@@ -225,6 +225,7 @@ public class EntitySelector { - - if (this.isWorldLimited()) { - object = source.getLevel().getPlayers(predicate, i); -+ ((List) object).removeIf(entityplayer3 -> !canSee(source, (ServerPlayer) entityplayer3)); // Purpur - Hide hidden players from entity selector - } else { - object = new ObjectArrayList(); - Iterator iterator = source.getServer().getPlayerList().getPlayers().iterator(); -@@ -232,7 +233,7 @@ public class EntitySelector { - while (iterator.hasNext()) { - ServerPlayer entityplayer2 = (ServerPlayer) iterator.next(); - -- if (predicate.test(entityplayer2)) { -+ if (predicate.test(entityplayer2) && canSee(source, entityplayer2)) { // Purpur - Hide hidden players from entity selector - ((List) object).add(entityplayer2); - if (((List) object).size() >= i) { - return (List) object; -@@ -299,4 +300,10 @@ public class EntitySelector { - public static Component joinNames(List entities) { - return ComponentUtils.formatList(entities, 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/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 3a0ed610320f9730806d463647f53857265aa2e8..f4e5d43625838f0dd6979e10a21ef58e8e0e1263 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -249,6 +249,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())); -@@ -261,6 +262,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/0157-Config-for-health-to-impact-Creeper-explosion-radius.patch b/patches/server/0157-Config-for-health-to-impact-Creeper-explosion-radius.patch deleted file mode 100644 index 8b94ea7c8b..0000000000 --- a/patches/server/0157-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 58ef6c538a33199e5a8423223c22c61bf91fccae..d22a36bdabf04da4c4fe6e4699c9d78dc5b4e0c6 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java -@@ -377,9 +377,10 @@ public class Creeper extends Monster { - - if (world instanceof ServerLevel worldserver) { - float f = this.isPowered() ? 2.0F : 1.0F; -+ float multiplier = worldserver.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 1ae95a30b6e4cf69028dc61c398651bde526418f..851d0c96350abc8d8c490d92183540b013a98d12 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -954,6 +954,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); -@@ -970,6 +971,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/0158-Iron-golem-calm-anger-options.patch b/patches/server/0158-Iron-golem-calm-anger-options.patch deleted file mode 100644 index f9046c4d03..0000000000 --- a/patches/server/0158-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 74971f19a646b78bff66d2543d80d9358fdd29c5..da51e119c290a850072679a7b4f4bde3025c95cf 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -95,6 +95,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 - Ridables -+ 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(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables - this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true)); - this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.9D, 32.0F)); -@@ -312,6 +313,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - - this.playSound(SoundEvents.IRON_GOLEM_REPAIR, 1.0F, f1); - itemstack.consume(1, player); -+ if (this.level().purpurConfig.ironGolemHealCalm && isAngry() && getHealth() == getMaxHealth()) stopBeingAngry(); // Purpur - Iron golem calm anger options - return InteractionResult.SUCCESS; - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 851d0c96350abc8d8c490d92183540b013a98d12..5caaaa88d71f84562f8ab403062bf9ac4eb4dfb9 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1442,6 +1442,8 @@ public class PurpurWorldConfig { - public double ironGolemMaxHealth = 100.0D; - public double ironGolemScale = 1.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); -@@ -1455,6 +1457,8 @@ public class PurpurWorldConfig { - 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); - } - - 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/0159-Breedable-parrots.patch b/patches/server/0159-Breedable-parrots.patch deleted file mode 100644 index 528b7a97aa..0000000000 --- a/patches/server/0159-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 3305a49fed395c134cf099253ea0558b6bd27bd4..eb7d6c1367ad9e4b8292987a69531cd627580167 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, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) { -@@ -369,13 +371,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/NewMinecartBehavior.java b/src/main/java/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java -index f43439b31a14b9db4744512465d81134ebe5b3e1..0b9741dfebd0bb95e8c0e1f55ce18dfb353f693a 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java -@@ -426,7 +426,7 @@ public class NewMinecartBehavior extends MinecartBehavior { - private Vec3 calculateBoostTrackSpeed(Vec3 velocity, BlockPos railPos, BlockState railState) { - if (railState.is(Blocks.POWERED_RAIL) && (Boolean) railState.getValue(PoweredRailBlock.POWERED)) { - if (velocity.length() > 0.01D) { -- return velocity.normalize().scale(velocity.length() + 0.06D); -+ return velocity.normalize().scale(velocity.length() + this.level().purpurConfig.poweredRailBoostModifier); // Purpur - } else { - Vec3 vec3d1 = this.minecart.getRedstoneDirection(railPos); - -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java b/src/main/java/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java -index 04a622f52353ebcc21f41c233f5a0fd67690cf4a..f10ce069ef427df16fd0ce0e60b85c805ca703f0 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java -@@ -310,9 +310,9 @@ public class OldMinecartBehavior extends MinecartBehavior { - vec3d5 = this.getDeltaMovement(); - d17 = vec3d5.horizontalDistance(); - if (d17 > 0.01D) { -- double d19 = 0.06D; -+ double d19 = world.purpurConfig.poweredRailBoostModifier; // Purpur - -- this.setDeltaMovement(vec3d5.add(vec3d5.x / d17 * 0.06D, 0.0D, vec3d5.z / d17 * 0.06D)); -+ this.setDeltaMovement(vec3d5.add(vec3d5.x / d17 * world.purpurConfig.poweredRailBoostModifier, 0.0D, vec3d5.z / d17 * world.purpurConfig.poweredRailBoostModifier)); // Purpur - } else { - Vec3 vec3d6 = this.getDeltaMovement(); - double d20 = vec3d6.x; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 3d226e2cd3d805a7f53f845f5b873506b6ef274a..fb10c66da40ba5e4d0eb5b567d177bbcb2c0fd4c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -270,6 +270,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); -@@ -322,6 +323,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/0161-Add-config-change-multiplier-critical-damage-value.patch b/patches/server/0161-Add-config-change-multiplier-critical-damage-value.patch deleted file mode 100644 index ea121b858d..0000000000 --- a/patches/server/0161-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 fc0e571b62a9bd40df2d3d066cf374e12004a6d8..bc3c67e8f70890831a0bb1799a3b7c10852b1273 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1309,7 +1309,7 @@ public abstract class Player extends LivingEntity { - flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits - if (flag2) { - damagesource = damagesource.critical(true); // Paper start - critical damage API -- f *= 1.5F; -+ f *= this.level().purpurConfig.playerCriticalDamageMultiplier; // Purpur - } - - float f3 = f + f1; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fb10c66da40ba5e4d0eb5b567d177bbcb2c0fd4c..b5712c7aa0de07736c7e6ae0686ed36ed3629235 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -367,6 +367,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); -@@ -386,6 +387,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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0162-Option-to-disable-dragon-egg-teleporting.patch b/patches/server/0162-Option-to-disable-dragon-egg-teleporting.patch deleted file mode 100644 index 2829d6e62f..0000000000 --- a/patches/server/0162-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 30d15686b1a81de7ac28feb0c6188eb007c6f2fd..b6799db00e157892dd4339a01d2ca36092c8e491 100644 ---- a/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/DragonEggBlock.java -@@ -48,8 +48,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 b5712c7aa0de07736c7e6ae0686ed36ed3629235..427e257d7e53d0dc5fbd4acbaa2c1d1f2a12fff8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -499,6 +499,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/0163-Config-for-unverified-username-message.patch b/patches/server/0163-Config-for-unverified-username-message.patch deleted file mode 100644 index 007140c040..0000000000 --- a/patches/server/0163-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 033755682c61c889723c3669b5cff4de147f637e..16069b9cbf6c7679c28a2e9a54e77d23cd10e541 100644 ---- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -322,7 +322,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, - ServerLoginPacketListenerImpl.LOGGER.warn("Failed to verify username but will let them in anyway!"); - ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(s1)); // 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 - ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", s1); - } - } catch (AuthenticationUnavailableException authenticationunavailableexception) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index f4e5d43625838f0dd6979e10a21ef58e8e0e1263..d3c58ccd12ee56ac0a086c54f2f589b6728bab54 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -187,6 +187,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); -@@ -199,6 +200,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/0164-Make-anvil-cumulative-cost-configurable.patch b/patches/server/0164-Make-anvil-cumulative-cost-configurable.patch deleted file mode 100644 index 4bcd6dbe50..0000000000 --- a/patches/server/0164-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 346681cf9c3c9ff5274f63236b0cc7c4eec76492..5959e1df9374bcb889be1726abb83b8c58962d05 100644 ---- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java -@@ -406,7 +406,7 @@ public class AnvilMenu extends ItemCombinerMenu { - } - - public static int calculateIncreasedRepairCost(int cost) { -- return (int) Math.min((long) cost * 2L + 1L, 2147483647L); -+ return org.purpurmc.purpur.PurpurConfig.anvilCumulativeCost ? (int) Math.min((long) cost * 2L + 1L, 2147483647L) : 0; // Purpur - Make anvil cumulative cost configurable - } - - 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 d3c58ccd12ee56ac0a086c54f2f589b6728bab54..52a8a0b6b78dae42c717cd8c34268d631eb3c1fe 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -272,6 +272,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); -@@ -304,6 +305,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 allowInapplicableEnchants = false; diff --git a/patches/server/0165-Bee-can-work-when-raining-or-at-night.patch b/patches/server/0165-Bee-can-work-when-raining-or-at-night.patch deleted file mode 100644 index ad3886d004..0000000000 --- a/patches/server/0165-Bee-can-work-when-raining-or-at-night.patch +++ /dev/null @@ -1,41 +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 40137a98174cd0238d06c894373984a403f96fbc..e191e27308dd8c5c8702692091541756f2b57ac1 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -430,7 +430,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - - public static boolean isNightOrRaining(Level world) { -- return world.dimensionType().hasSkyLight() && (world.isNight() || world.isRaining()); -+ return world.dimensionType().hasSkyLight() && ((world.isNight() && !world.purpurConfig.beeCanWorkAtNight) || (world.isRaining() && !world.purpurConfig.beeCanWorkInRain)); // Purpur - } - - public void setStayOutOfHiveCountdown(int cannotEnterHiveTicks) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 427e257d7e53d0dc5fbd4acbaa2c1d1f2a12fff8..8eaff3384ed2fa6b4f557e82b6c0f89fa8ae7cb4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -750,6 +750,8 @@ public class PurpurWorldConfig { - public double beeScale = 1.0D; - public int beeBreedingTicks = 6000; - public boolean beeTakeDamageFromWater = true; -+ 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); -@@ -764,6 +766,8 @@ public class PurpurWorldConfig { - beeScale = Mth.clamp(getDouble("mobs.bee.attributes.scale", beeScale), 0.0625D, 16.0D); - 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/0166-API-for-any-mob-to-burn-daylight.patch b/patches/server/0166-API-for-any-mob-to-burn-daylight.patch deleted file mode 100644 index ff515fb0fb..0000000000 --- a/patches/server/0166-API-for-any-mob-to-burn-daylight.patch +++ /dev/null @@ -1,388 +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 a5f4f5d915a864240fd738ca32872829bfcabb41..25a6f228bad7deca7e7301868039d27bf65505c8 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -575,6 +575,22 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - // Purpur end - Add canSaveToDisk to Entity - -+ // Purpur start - copied from Mob - API for any mob to burn daylight -+ 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; -+ } -+ // Purpur end - copied from Mob - API for any mob to burn daylight -+ - public Entity(EntityType type, Level world) { - this.id = Entity.ENTITY_COUNTER.incrementAndGet(); - this.despawnTime = type == EntityType.PLAYER ? -1 : world.paperConfig().entities.spawning.despawnTime.getOrDefault(type, io.papermc.paper.configuration.type.number.IntOr.Disabled.DISABLED).or(-1); // Paper - entity despawn time limit -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 28d3b200ab0c3e8993dfb89a3e48146898c6b85e..ecf8e9742951c144170ce818c14d368011ef99a8 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -295,6 +295,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 - Friction API -+ 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 - - @Override - public float getBukkitYaw() { -@@ -839,6 +840,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> { - nbt.put("Brain", nbtbase); - }); -+ nbt.putBoolean("Purpur.ShouldBurnInDay", this.shouldBurnInDay); // Purpur - API for any mob to burn daylight - } - - @Override -@@ -927,6 +929,11 @@ public abstract class LivingEntity extends Entity implements Attackable { - this.brain = this.makeBrain(new Dynamic(NbtOps.INSTANCE, nbt.get("Brain"))); - } - -+ // Purpur start - API for any mob to burn daylight -+ if (nbt.contains("Purpur.ShouldBurnInDay")) { -+ this.shouldBurnInDay = nbt.getBoolean("Purpur.ShouldBurnInDay"); -+ } -+ // Purpur end - API for any mob to burn daylight - } - - // CraftBukkit start -@@ -3788,6 +3795,34 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - } - -+ // Purpur start - copied from Zombie - API for any mob to burn daylight -+ if (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) { -+ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); -+ -+ if (!itemstack.isEmpty()) { -+ if (itemstack.isDamageableItem()) { -+ Item item = itemstack.getItem(); -+ -+ itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2)); -+ if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) { -+ this.onEquippedItemBroken(item, EquipmentSlot.HEAD); -+ this.setItemSlot(EquipmentSlot.HEAD, 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); -+ } -+ } -+ } -+ // Purpur end - copied from Zombie - API for any mob to burn daylight - } - - 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 6836f0ddab9d05d146fc03c234b1b58829ec96c0..30213b1917de318989f280aed8735bbe539de100 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -1773,17 +1773,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - protected void playAttackSound() {} - - 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; -+ // Purpur - implemented in Entity - API for any mob to burn daylight -+ 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 22b003a23b519bedc50bbcad0706aa2d7d7f4b3a..27bdd3c4e0dc3fbb906689e2390c945bf3d40eea 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -@@ -70,6 +70,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - protected AbstractSkeleton(EntityType type, Level world) { - super(type, world); - this.reassessWeaponGoal(); -+ this.setShouldBurnInDay(true); // Purpur - API for any mob to burn daylight - } - - @Override -@@ -100,37 +101,14 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - abstract SoundEvent getStepSound(); - - // Paper start - shouldBurnInDay API -- private boolean shouldBurnInDay = true; -+ //private boolean shouldBurnInDay = true; // Purpur - moved to LivingEntity; keep methods for ABI compatibility - API for any mob to burn daylight - public boolean shouldBurnInDay() { return shouldBurnInDay; } - public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } - // Paper end - shouldBurnInDay API - - @Override - public void aiStep() { -- boolean flag = shouldBurnInDay && this.isSunBurnTick(); // Paper - shouldBurnInDay API -- -- if (flag) { -- ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); -- -- if (!itemstack.isEmpty()) { -- if (itemstack.isDamageableItem()) { -- Item item = itemstack.getItem(); -- -- itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2)); -- if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) { -- this.onEquippedItemBroken(item, EquipmentSlot.HEAD); -- this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY); -- } -- } -- -- flag = false; -- } -- -- if (flag) { -- this.igniteForSeconds(8.0F); -- } -- } -- -+ // Purpur - implemented in LivingEntity - API for any mob to burn daylight - super.aiStep(); - } - -@@ -252,7 +230,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - super.readAdditionalSaveData(nbt); - this.reassessWeaponGoal(); - // Paper start - shouldBurnInDay API -- if (nbt.contains("Paper.ShouldBurnInDay")) { -+ if (false && nbt.contains("Paper.ShouldBurnInDay")) { // Purpur - implemented in LivingEntity - API for any mob to burn daylight - this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); - } - // Paper end - shouldBurnInDay API -@@ -262,7 +240,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 - API for any mob to burn daylight - } - // Paper end - shouldBurnInDay API - -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 43887b5aa533d456de5e66da2a3bef82aed58084..9f0b91de19ecdb0f74fe6d93a1e75c27c262ee07 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Husk.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java -@@ -21,6 +21,7 @@ public class Husk extends Zombie { - - public Husk(EntityType type, Level world) { - super(type, world); -+ this.setShouldBurnInDay(false); // Purpur - API for any mob to burn daylight - } - - // Purpur start - Ridables -@@ -79,7 +80,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/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index f09830c1c88f4b28f05e1647706a3c96a596ad1e..71bbb3209acc12c9f20b8964770be8666a7e72f8 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 - API for any mob to burn daylight - } - - // Purpur start - Ridables -@@ -251,6 +252,7 @@ public class Phantom extends FlyingMob implements Enemy { - - @Override - public void aiStep() { -+ // Purpur - implemented in LivingEntity; moved down to shouldBurnInDay() - API for any mob to burn daylight - // Purpur start - Phantoms burn in light - boolean burnFromDaylight = this.shouldBurnInDay && this.isSunBurnTick() && this.level().purpurConfig.phantomBurnInDaylight; - boolean burnFromLightSource = this.level().purpurConfig.phantomBurnInLight > 0 && this.level().getMaxLocalRawBrightness(blockPosition()) >= this.level().purpurConfig.phantomBurnInLight; -@@ -282,7 +284,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 - API for any mob to burn daylight - this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); - } - // Paper end -@@ -299,7 +301,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 - API for any mob to burn daylight - // Paper end - } - -@@ -359,8 +361,14 @@ public class Phantom extends FlyingMob implements Enemy { - return this.spawningEntity; - } - 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 - API for any mob to burn daylight -+ // Purpur start - API for any mob to burn daylight -+ 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 - API for any mob to burn daylight - public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } - // Paper end - -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 09b21cf02a07e1eb6a0aa2c6880d4106fe9e0d77..d0fcd51e36e7c7e774fcf9b1db42ec7fceb9fc41 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -99,11 +99,12 @@ public class Zombie extends Monster { - private int inWaterTime; - public int conversionTime; - // private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field // Paper - remove anti tick skipping measures / wall time -- 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 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 - Configurable door breaking difficulty -+ this.setShouldBurnInDay(true); // Purpur - API for any mob to burn daylight - } - - public Zombie(Level world) { -@@ -296,32 +297,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()) { -- Item item = itemstack.getItem(); -- -- itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2)); -- if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) { -- this.onEquippedItemBroken(item, EquipmentSlot.HEAD); -- this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY); -- } -- } -- -- flag = false; -- } -- -- if (flag) { -- this.igniteForSeconds(8.0F); -- } -- } -- } -- -+ // Purpur - implemented in LivingEntity - API for any mob to burn daylight - super.aiStep(); - } - -@@ -381,6 +357,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 - } -@@ -519,7 +496,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 - Add more Zombie API -+ //nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper - Add more Zombie API // Purpur - implemented in LivingEntity - API for any mob to burn daylight - } - - @Override -@@ -532,7 +509,7 @@ public class Zombie extends Monster { - this.startUnderWaterConversion(nbt.getInt("DrownedConversionTime")); - } - // Paper start - Add more Zombie API -- if (nbt.contains("Paper.ShouldBurnInDay")) { -+ if (false && nbt.contains("Paper.ShouldBurnInDay")) { // Purpur - implemented in LivingEntity - API for any mob to burn daylight - this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); - } - // Paper end - Add more Zombie API -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index 51772356a3c64da7b29fa204ffec03a70cd4406a..f9d7f1d317a9534d471a481db8c26f9d3b15bfda 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -87,6 +87,13 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - this.entityType = CraftEntityType.minecraftToBukkit(entity.getType()); - } - -+ // 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 4f98d138a275a6c34528b7a5148ef265bc38d6b5..7ccc40555964b906be6987532de1f319e38741ce 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -1211,4 +1211,16 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - return this.getHandle().canUseSlot(org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot)); - } - // Paper end - Expose canUseSlot -+ -+ // 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/patches/server/0167-Config-MobEffect-by-world.patch b/patches/server/0167-Config-MobEffect-by-world.patch deleted file mode 100644 index 04bd2172b0..0000000000 --- a/patches/server/0167-Config-MobEffect-by-world.patch +++ /dev/null @@ -1,99 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: DoctaEnkoda -Date: Sat, 23 Sep 2023 03:59:15 -0700 -Subject: [PATCH] Config MobEffect by world - - -diff --git a/src/main/java/net/minecraft/world/effect/HungerMobEffect.java b/src/main/java/net/minecraft/world/effect/HungerMobEffect.java -index 11d1ee8fae7670f02cb3f5d57f4774dbde77f48c..88cf1353892a7ead4e0f16822216b151726ac0e4 100644 ---- a/src/main/java/net/minecraft/world/effect/HungerMobEffect.java -+++ b/src/main/java/net/minecraft/world/effect/HungerMobEffect.java -@@ -13,7 +13,7 @@ class HungerMobEffect extends MobEffect { - @Override - public boolean applyEffectTick(ServerLevel world, LivingEntity entity, int amplifier) { - if (entity instanceof Player entityhuman) { -- entityhuman.causeFoodExhaustion(0.005F * (float) (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent -+ entityhuman.causeFoodExhaustion(entity.level().purpurConfig.humanHungerExhaustionAmount * (float) (amplifier + 1), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.HUNGER_EFFECT); // CraftBukkit - EntityExhaustionEvent // Purpur - } - - return true; -diff --git a/src/main/java/net/minecraft/world/effect/PoisonMobEffect.java b/src/main/java/net/minecraft/world/effect/PoisonMobEffect.java -index 3f1e59229d6a71117700a56c87914bc9401f1da2..32c8474ffa88e997bb484caecb21e3dc98991e70 100644 ---- a/src/main/java/net/minecraft/world/effect/PoisonMobEffect.java -+++ b/src/main/java/net/minecraft/world/effect/PoisonMobEffect.java -@@ -13,8 +13,8 @@ public class PoisonMobEffect extends MobEffect { - - @Override - public boolean applyEffectTick(ServerLevel world, LivingEntity entity, int amplifier) { -- if (entity.getHealth() > 1.0F) { -- entity.hurtServer(world, entity.damageSources().poison(), 1.0F); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON -+ if (entity.getHealth() > entity.level().purpurConfig.entityMinimalHealthPoison) { // Purpur -+ entity.hurtServer(world, entity.damageSources().poison(), entity.level().purpurConfig.entityPoisonDegenerationAmount); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON // Purpur - } - - return true; -diff --git a/src/main/java/net/minecraft/world/effect/RegenerationMobEffect.java b/src/main/java/net/minecraft/world/effect/RegenerationMobEffect.java -index b43e573e91d1f1b407774bb2d60ee5f099f171e7..25aa932fc03eeebc5aabca6c5eae0cbfc8ad8396 100644 ---- a/src/main/java/net/minecraft/world/effect/RegenerationMobEffect.java -+++ b/src/main/java/net/minecraft/world/effect/RegenerationMobEffect.java -@@ -12,7 +12,7 @@ class RegenerationMobEffect extends MobEffect { - @Override - public boolean applyEffectTick(ServerLevel world, 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 - } - - return true; -diff --git a/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java b/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java -index 98b74649a667fb9b10afef0ba5383a73022d8c71..837bdc7d6bd4e05b0deded829c678c86ae3d79d5 100644 ---- a/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java -+++ b/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java -@@ -21,7 +21,7 @@ class SaturationMobEffect extends InstantenousMobEffect { - 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/net/minecraft/world/effect/WitherMobEffect.java b/src/main/java/net/minecraft/world/effect/WitherMobEffect.java -index 55132e6e064ddd15b26286eca335305ed57b2f9e..1e04947995009689315352b79989e7ce4e20073c 100644 ---- a/src/main/java/net/minecraft/world/effect/WitherMobEffect.java -+++ b/src/main/java/net/minecraft/world/effect/WitherMobEffect.java -@@ -12,7 +12,7 @@ public class WitherMobEffect extends MobEffect { - - @Override - public boolean applyEffectTick(ServerLevel world, LivingEntity entity, int amplifier) { -- entity.hurtServer(world, entity.damageSources().wither(), 1.0F); -+ entity.hurtServer(world, entity.damageSources().wither(), entity.level().purpurConfig.entityWitherDegenerationAmount); // Purpur - return true; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 8eaff3384ed2fa6b4f557e82b6c0f89fa8ae7cb4..b279cd51d5661c304bff6a2638bf39df0e3cf952 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -326,6 +326,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/0168-Beacon-Activation-Range-Configurable.patch b/patches/server/0168-Beacon-Activation-Range-Configurable.patch deleted file mode 100644 index cce3d7713e..0000000000 --- a/patches/server/0168-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 0e0d178f2793ab014358f534c8dc53218b89f083..15a24cad5700cc1fe7430e425909869be84fe928 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 -@@ -92,6 +92,16 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name - - 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; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b279cd51d5661c304bff6a2638bf39df0e3cf952..2b5543f070925421e3b924e373a9610fd9b24f2a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -466,6 +466,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/0169-Make-lightning-rod-range-configurable.patch b/patches/server/0169-Make-lightning-rod-range-configurable.patch deleted file mode 100644 index d93da1f1b9..0000000000 --- a/patches/server/0169-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 56fe25076109827815d7d8ebc45a62d391462bbf..9ff7fb18e52bea06b02ebf0d1b97647df6b304dc 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1073,7 +1073,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - 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 52a8a0b6b78dae42c717cd8c34268d631eb3c1fe..f8028be8c5ac3349c8b4b5a337108e305765cc2e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -273,6 +273,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); -@@ -306,6 +307,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 allowInapplicableEnchants = false; diff --git a/patches/server/0170-Burp-delay-burp-after-eating-food-fills-hunger-bar-c.patch b/patches/server/0170-Burp-delay-burp-after-eating-food-fills-hunger-bar-c.patch deleted file mode 100644 index 291da503d4..0000000000 --- a/patches/server/0170-Burp-delay-burp-after-eating-food-fills-hunger-bar-c.patch +++ /dev/null @@ -1,103 +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/effect/SaturationMobEffect.java b/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java -index 837bdc7d6bd4e05b0deded829c678c86ae3d79d5..0c7c0524e487ff32e16dd9939d92bc6441602747 100644 ---- a/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java -+++ b/src/main/java/net/minecraft/world/effect/SaturationMobEffect.java -@@ -21,6 +21,7 @@ class SaturationMobEffect extends InstantenousMobEffect { - int oldFoodLevel = entityhuman.getFoodData().foodLevel; - org.bukkit.event.entity.FoodLevelChangeEvent event = CraftEventFactory.callFoodLevelChangeEvent(entityhuman, amplifier + 1 + oldFoodLevel); - if (!event.isCancelled()) { -+ if (entityhuman.level().purpurConfig.playerBurpWhenFull && event.getFoodLevel() == 20 && oldFoodLevel < 20) entityhuman.burpDelay = entityhuman.level().purpurConfig.playerBurpDelay; // Purpur - entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, entity.level().purpurConfig.humanSaturationRegenAmount); // Purpur - } - -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 bc3c67e8f70890831a0bb1799a3b7c10852b1273..a8b5869160cb75c314459403a0a229ec68b69086 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -201,6 +201,7 @@ public abstract class Player extends LivingEntity { - 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 sixRowEnderchestSlotCount = -1; // Purpur -+ public int burpDelay = 0; // Purpur - - // CraftBukkit start - public boolean fauxSleeping; -@@ -276,6 +277,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.isPassenger()) { - this.setOnGround(false); -diff --git a/src/main/java/net/minecraft/world/food/FoodData.java b/src/main/java/net/minecraft/world/food/FoodData.java -index 6a686be6a69ae890d519a54ca099d4ba14e5b9e1..4f8ee2e5db3352306f3c035052866d95630f4aaf 100644 ---- a/src/main/java/net/minecraft/world/food/FoodData.java -+++ b/src/main/java/net/minecraft/world/food/FoodData.java -@@ -44,6 +44,7 @@ public class FoodData { - org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityplayer, foodinfo.nutrition() + oldFoodLevel, itemstack); - - if (!event.isCancelled()) { -+ if (entityplayer.level().purpurConfig.playerBurpWhenFull && event.getFoodLevel() == 20 && oldFoodLevel < 20) entityplayer.burpDelay = entityplayer.level().purpurConfig.playerBurpDelay; // Purpur - this.add(event.getFoodLevel() - oldFoodLevel, foodinfo.saturation()); - } - -diff --git a/src/main/java/net/minecraft/world/food/FoodProperties.java b/src/main/java/net/minecraft/world/food/FoodProperties.java -index 882b72799ae532f4e181214d5756ec024af223e2..9a3f2a95debcf8b94f7deb375922ea09b30aabab 100644 ---- a/src/main/java/net/minecraft/world/food/FoodProperties.java -+++ b/src/main/java/net/minecraft/world/food/FoodProperties.java -@@ -33,7 +33,7 @@ public record FoodProperties(int nutrition, float saturation, boolean canAlwaysE - world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), (SoundEvent) consumable.sound().value(), SoundSource.NEUTRAL, 1.0F, randomsource.triangle(1.0F, 0.4F)); - if (user instanceof Player entityhuman) { - entityhuman.getFoodData().eat(this, stack, (ServerPlayer) entityhuman); // CraftBukkit -- world.playSound((Player) null, entityhuman.getX(), entityhuman.getY(), entityhuman.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, Mth.randomBetween(randomsource, 0.9F, 1.0F)); -+ //world.playSound((Player) null, entityhuman.getX(), entityhuman.getY(), entityhuman.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, Mth.randomBetween(randomsource, 0.9F, 1.0F)); // Purpur - moved to Player#tick() - } - - } -diff --git a/src/main/java/net/minecraft/world/level/block/CakeBlock.java b/src/main/java/net/minecraft/world/level/block/CakeBlock.java -index 648c2510beb162e73aed236a3169d0bbb8fc5050..3563a241c0b697dc0167cf7b1aa73fef7d1e7934 100644 ---- a/src/main/java/net/minecraft/world/level/block/CakeBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CakeBlock.java -@@ -119,6 +119,7 @@ public class CakeBlock extends Block { - 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 - player.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 0.1F); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 2b5543f070925421e3b924e373a9610fd9b24f2a..849532c99cfad2870fd22be6e759e153e8fae10b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -383,6 +383,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); -@@ -403,6 +405,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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0171-Allow-player-join-full-server-by-permission.patch b/patches/server/0171-Allow-player-join-full-server-by-permission.patch deleted file mode 100644 index e49803d32d..0000000000 --- a/patches/server/0171-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 0969a713df3150b0b2d2dd2d595e5f49e491f8af..5b1705794a8c3914cb11fdd35f75c8e0c128ecd0 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -711,7 +711,7 @@ public abstract class PlayerList { - event.disallow(PlayerLoginEvent.Result.KICK_BANNED, io.papermc.paper.adventure.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/0172-Add-portal-permission-bypass.patch b/patches/server/0172-Add-portal-permission-bypass.patch deleted file mode 100644 index 193fdeefd3..0000000000 --- a/patches/server/0172-Add-portal-permission-bypass.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: William Blake Galbreath -Date: Thu, 7 Dec 2023 14:53:48 -0800 -Subject: [PATCH] Add portal 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 a8b5869160cb75c314459403a0a229ec68b69086..ee88c493119a4a4629d6456c8698dd89e4f477f4 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -202,6 +202,7 @@ public abstract class Player extends LivingEntity { - public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; // Paper - flying fall damage - public int sixRowEnderchestSlotCount = -1; // Purpur - public int burpDelay = 0; // Purpur -+ public boolean canPortalInstant = false; // Purpur - - // CraftBukkit start - public boolean fauxSleeping; -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 2d53c57c961fa8977e37931775863665381595eb..6c10860aee8b8fac4089fb7f1506c890fb3f5308 100644 ---- a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java -@@ -137,7 +137,7 @@ public class NetherPortalBlock extends Block implements Portal { - @Override - public int getPortalTransitionTime(ServerLevel world, Entity entity) { - if (entity instanceof Player entityhuman) { -- return Math.max(0, world.getGameRules().getInt(entityhuman.getAbilities().invulnerable ? GameRules.RULE_PLAYERS_NETHER_PORTAL_CREATIVE_DELAY : GameRules.RULE_PLAYERS_NETHER_PORTAL_DEFAULT_DELAY)); -+ return Math.max(0, entityhuman.canPortalInstant ? 1 : world.getGameRules().getInt(entityhuman.getAbilities().invulnerable ? GameRules.RULE_PLAYERS_NETHER_PORTAL_CREATIVE_DELAY : GameRules.RULE_PLAYERS_NETHER_PORTAL_DEFAULT_DELAY)); // Purpur - } else { - return 0; - } -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java -index e345cdbfab44a0f5da80d738798dbb4424b7ab5c..856f12eb276c214f2f57a58a89a4da9eea34db2d 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java -@@ -273,6 +273,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/patches/server/0173-Shulker-spawn-from-bullet-options.patch b/patches/server/0173-Shulker-spawn-from-bullet-options.patch deleted file mode 100644 index 806e99e22f..0000000000 --- a/patches/server/0173-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 2b2e68e1449606ecfe5c414834ce3e2c6912d820..1957e2be5087e7bf85be5dfba53de8385dbeadd6 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -512,12 +512,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(), EntitySpawnReason.BREEDING); -+ // Purpur end - - if (entityshulker != null) { - entityshulker.setVariant(this.getVariant()); -@@ -631,7 +640,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 648e6979aa72b9d1e1ea3b40d5a876e3c690b934..d9cd4f47e9990bdd85e30f68ca3b755a953332a1 100644 ---- a/src/main/java/net/minecraft/world/item/DyeColor.java -+++ b/src/main/java/net/minecraft/world/item/DyeColor.java -@@ -123,4 +123,10 @@ public enum DyeColor implements StringRepresentable { - 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 -+ 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 849532c99cfad2870fd22be6e759e153e8fae10b..51609ddc3890b5a6a9106b6d2960697723651ec2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1993,6 +1993,11 @@ public class PurpurWorldConfig { - 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; - private void shulkerSettings() { - shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable); - shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater); -@@ -2005,6 +2010,11 @@ public class PurpurWorldConfig { - 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); - } - - public boolean silverfishRidable = false; diff --git a/patches/server/0174-Eating-glow-berries-adds-glow-effect.patch b/patches/server/0174-Eating-glow-berries-adds-glow-effect.patch deleted file mode 100644 index 80fd266a98..0000000000 --- a/patches/server/0174-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 41fbe61268c0a16078b5f846ab12bde172872ff7..e2f1d3f349e3f82e7a565363c42818e5cf6c4d61 100644 ---- a/src/main/java/net/minecraft/world/item/Items.java -+++ b/src/main/java/net/minecraft/world/item/Items.java -@@ -1987,7 +1987,7 @@ public class Items { - "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, settings -> settings.component(DataComponents.CONTAINER, ItemContainerContents.EMPTY)); - public static final Item SOUL_CAMPFIRE = registerBlock( -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 51609ddc3890b5a6a9106b6d2960697723651ec2..1e5bb7dc174bd43479534a9cab0b3bf05695ae51 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -214,6 +214,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 -> { -@@ -260,6 +261,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..b257f35caa13b660854cf17f41fd8fba1d56c458 ---- /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.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/patches/server/0175-Option-to-make-drowned-break-doors.patch b/patches/server/0175-Option-to-make-drowned-break-doors.patch deleted file mode 100644 index 1a2a6415d8..0000000000 --- a/patches/server/0175-Option-to-make-drowned-break-doors.patch +++ /dev/null @@ -1,38 +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 7651c29a6468e93bd10d58f76a86a68912509bd3..1e4208f720bfafe9542691b826e4b8dccd449ef3 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -133,6 +133,7 @@ public class Drowned extends Zombie implements RangedAttackMob { - 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)); - 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, (target, world) -> this.okTarget(target))); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1e5bb7dc174bd43479534a9cab0b3bf05695ae51..7d4570b6cc775e73a64e0a7ec4fa15397636a0f3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1083,6 +1083,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); -@@ -1099,6 +1100,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/0176-Configurable-hunger-starvation-damage.patch b/patches/server/0176-Configurable-hunger-starvation-damage.patch deleted file mode 100644 index c0ab2737d5..0000000000 --- a/patches/server/0176-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 4f8ee2e5db3352306f3c035052866d95630f4aaf..26309851107fb80712b2dc7c5e6112cedf929003 100644 ---- a/src/main/java/net/minecraft/world/food/FoodData.java -+++ b/src/main/java/net/minecraft/world/food/FoodData.java -@@ -97,7 +97,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.hurtServer(worldserver, player.damageSources().starve(), 1.0F); -+ player.hurtServer(worldserver, player.damageSources().starve(), player.level().purpurConfig.hungerStarvationDamage); // Purpur - Configurable hunger starvation damage - } - - this.tickTimer = 0; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 7d4570b6cc775e73a64e0a7ec4fa15397636a0f3..d78c39b9db77ee6948fd87c664ab5fa5b61367ea 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2705,4 +2705,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/0178-Add-uptime-command.patch b/patches/server/0178-Add-uptime-command.patch deleted file mode 100644 index 42b5381c5c..0000000000 --- a/patches/server/0178-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 f01fff591efc92267d96084660f9e9688bd65795..357f2b078a1bd4730599d6d3f5ac3c6f859d3d86 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -258,6 +258,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 7ce4148a1fcd221bf22f4ad02a2fc32e007fa1da..9219b31a7273b08e7acd1a953c260a5520333922 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -314,6 +314,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); - public int autosavePeriod; - // Paper - don't store the vanilla dispatcher -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index f8028be8c5ac3349c8b4b5a337108e305765cc2e..e6079b44bd1364bd6be93d584634bab3553d5edb 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -187,6 +187,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); -@@ -200,6 +201,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); - } - -@@ -252,6 +254,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())); -@@ -265,6 +276,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/0179-Tool-actionable-options.patch b/patches/server/0179-Tool-actionable-options.patch deleted file mode 100644 index a43e11c7b2..0000000000 --- a/patches/server/0179-Tool-actionable-options.patch +++ /dev/null @@ -1,584 +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 abff08f2d61014944235ffe2f5494a718a28cc10..dc2c415ab227e1357533079ada4903e9f69d4f55 100644 ---- a/src/main/java/net/minecraft/world/item/AxeItem.java -+++ b/src/main/java/net/minecraft/world/item/AxeItem.java -@@ -62,13 +62,15 @@ public class AxeItem extends DiggerItem { - if (playerHasShieldUseIntent(context)) { - return InteractionResult.PASS; - } else { -- Optional optional = this.evaluateNewBlockState(level, blockPos, player, level.getBlockState(blockPos)); -+ Optional optional = this.evaluateActionable(level, blockPos, player, level.getBlockState(blockPos)); // Purpur - if (optional.isEmpty()) { - return InteractionResult.PASS; - } else { -+ org.purpurmc.purpur.tool.Actionable actionable = optional.get(); // Purpur -+ BlockState state = actionable.into().withPropertiesOf(level.getBlockState(blockPos)); // Purpur - ItemStack itemStack = context.getItemInHand(); - // Paper start - EntityChangeBlockEvent -- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, blockPos, optional.get())) { -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, blockPos, state)) { // Purpur - return InteractionResult.PASS; - } - // Paper end -@@ -76,8 +78,15 @@ public class AxeItem extends DiggerItem { - CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger((ServerPlayer)player, blockPos, itemStack); - } - -- level.setBlock(blockPos, optional.get(), 11); -- level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(player, optional.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, LivingEntity.getSlotForHand(context.getHand())); - } -@@ -92,22 +101,24 @@ public class AxeItem extends DiggerItem { - return context.getHand().equals(InteractionHand.MAIN_HAND) && player.getOffhandItem().is(Items.SHIELD) && !player.isSecondaryUseActive(); - } - -- private Optional evaluateNewBlockState(Level world, BlockPos pos, @Nullable Player player, BlockState state) { -- Optional optional = this.getStripped(state); -+ private Optional evaluateActionable(Level world, BlockPos pos, @Nullable Player player, BlockState state) { // Purpur -+ Optional optional = Optional.ofNullable(world.purpurConfig.axeStrippables.get(state.getBlock())); // Purpur - if (optional.isPresent()) { -- world.playSound(player, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); -+ world.playSound(STRIPPABLES.containsKey(state.getBlock()) ? player : null, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound - return optional; - } else { -- Optional optional2 = WeatheringCopper.getPrevious(state); -+ Optional optional2 = Optional.ofNullable(world.purpurConfig.axeWeatherables.get(state.getBlock())); // Purpur - if (optional2.isPresent()) { -- world.playSound(player, pos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0F, 1.0F); -+ world.playSound(WeatheringCopper.getPrevious(state).isPresent() ? player : null, pos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound - world.levelEvent(player, 3005, pos, 0); - return optional2; - } else { -- Optional optional3 = Optional.ofNullable(HoneycombItem.WAX_OFF_BY_BLOCK.get().get(state.getBlock())) -- .map(block -> block.withPropertiesOf(state)); -+ // Purpur start -+ Optional optional3 = Optional.ofNullable(world.purpurConfig.axeWaxables.get(state.getBlock())); -+ // .map(block -> block.withPropertiesOf(state)); -+ // Purpur end - if (optional3.isPresent()) { -- world.playSound(player, pos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0F, 1.0F); -+ world.playSound(HoneycombItem.WAX_OFF_BY_BLOCK.get().containsKey(state.getBlock()) ? player : null, pos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - force sound - world.levelEvent(player, 3004, pos, 0); - return optional3; - } else { -diff --git a/src/main/java/net/minecraft/world/item/HoeItem.java b/src/main/java/net/minecraft/world/item/HoeItem.java -index d2871bb4fd670ae4133d13f290b3256c9177d8e6..0936bdc945f73c7750c20a34276aead2921eeb61 100644 ---- a/src/main/java/net/minecraft/world/item/HoeItem.java -+++ b/src/main/java/net/minecraft/world/item/HoeItem.java -@@ -46,15 +46,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) { -diff --git a/src/main/java/net/minecraft/world/item/ShovelItem.java b/src/main/java/net/minecraft/world/item/ShovelItem.java -index 55c18f182166f4905d623d6f5e909eefd5ed2483..d10c4705cc9e7faabd4a5619e1da107231bdb37e 100644 ---- a/src/main/java/net/minecraft/world/item/ShovelItem.java -+++ b/src/main/java/net/minecraft/world/item/ShovelItem.java -@@ -47,9 +47,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()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d78c39b9db77ee6948fd87c664ab5fa5b61367ea..f277d8b257bf66552bfbd0ce98af2aa1e93779b7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -465,6 +465,287 @@ public class PurpurWorldConfig { - 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<>(); -+ 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 < 38) { -+ 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())); -+ } -+ 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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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()))) -+ ).forEach((blockId, obj) -> { -+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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()))) -+ ).forEach((blockId, obj) -> { -+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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)); -+ }); -+ } -+ - 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/0180-Store-placer-on-Block-when-placed.patch b/patches/server/0180-Store-placer-on-Block-when-placed.patch deleted file mode 100644 index fb8ce08f2d..0000000000 --- a/patches/server/0180-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 aac771d7d8aefc6d5fcb497763ab9bac1ce2dc31..a9fcade014e24f7967b003dcc45d290e3f456034 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -508,6 +508,7 @@ public final class ItemStack implements DataComponentHolder { - world.isBlockPlaceCancelled = true; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent - for (BlockState blockstate : blocks) { - blockstate.update(true, false); -+ ((CraftBlock) blockstate.getBlock()).getNMS().getBlock().forgetPlacer(); // Purpur - Store placer on Block when placed - } - world.isBlockPlaceCancelled = false; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent - world.preventPoiUpdated = false; -@@ -540,6 +541,7 @@ public final class ItemStack implements DataComponentHolder { - if (!(block.getBlock() instanceof BaseEntityBlock)) { // Containers get placed automatically - block.onPlace(world, newblockposition, oldBlock, true, context); - } -+ block.getBlock().forgetPlacer(); // Purpur - Store placer on Block when placed - - 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 c0b1f903962b25d8ff6c2b4fcd2be0e45de09b35..b88326cc6c54bf38a37f2491bfb2f0e0deb2b1df 100644 ---- a/src/main/java/net/minecraft/world/level/block/Block.java -+++ b/src/main/java/net/minecraft/world/level/block/Block.java -@@ -433,7 +433,17 @@ public class Block extends BlockBehaviour implements ItemLike { - } // Paper - fix drops not preventing stats/food exhaustion - } - -- public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {} -+ // Purpur start - Store placer on Block when placed -+ @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 - Store placer on Block when placed - - public boolean isPossibleToRespawnInThis(BlockState state) { - return !state.isSolid() && !state.liquid(); diff --git a/patches/server/0181-Summoner-API.patch b/patches/server/0181-Summoner-API.patch deleted file mode 100644 index eef9471a58..0000000000 --- a/patches/server/0181-Summoner-API.patch +++ /dev/null @@ -1,258 +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 da51e119c290a850072679a7b4f4bde3025c95cf..2cde18232b4101a5b20ca7897c0b8638eab68169 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -@@ -57,6 +57,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); -@@ -92,6 +93,16 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - return this.level().purpurConfig.ironGolemTakeDamageFromWater; - } - // Purpur end - Toggle for water sensitive mob damage -+ // Purpur start - Summoner API -+ @Nullable -+ public UUID getSummoner() { -+ return summoner; -+ } -+ -+ public void setSummoner(@Nullable UUID summoner) { -+ this.summoner = summoner; -+ } -+ // Purpur end - Summoner API - @Override - protected void registerGoals() { - if (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur - Ridables -@@ -169,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 - Summoner API - this.addPersistentAngerSaveData(nbt); - } - -@@ -176,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 - Summoner API - 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 f27f68750221852e55b92395530e00e9c6918551..b9d2d942194784de64665e9d9d851f4027aa4db2 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java -@@ -50,6 +50,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; -+ @Nullable private java.util.UUID summoner; // Purpur - Summoner API - - public SnowGolem(EntityType type, Level world) { - super(type, world); -@@ -79,6 +80,16 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.snowGolemScale); - } - // Purpur end - Configurable entity base attributes -+ // Purpur start - Summoner API -+ @Nullable -+ public java.util.UUID getSummoner() { -+ return summoner; -+ } -+ -+ public void setSummoner(@Nullable java.util.UUID summoner) { -+ this.summoner = summoner; -+ } -+ // Purpur end - Summoner API - @Override - protected void registerGoals() { - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables -@@ -106,6 +117,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 - Summoner API - } - - @Override -@@ -114,6 +126,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 - Summoner API - - } - -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 7bdc2b79fbf58f248caf63008ce898b9a43bfcd3..5f40d6d5b002f1f098245c3aa1174c85759191ce 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 -@@ -87,6 +87,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { - }; - private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0D).selector(WitherBoss.LIVING_ENTITY_SELECTOR); - private int shootCooldown = 0; // Purpur - Ridables -+ @Nullable private java.util.UUID summoner; // Purpur - Summoner API - // Paper start - private boolean canPortal = false; - -@@ -126,6 +127,16 @@ public class WitherBoss extends Monster implements RangedAttackMob { - return this.level().purpurConfig.witherTakeDamageFromWater; - } - // Purpur end - Toggle for water sensitive mob damage -+ // Purpur start - Summoner API -+ @Nullable -+ public java.util.UUID getSummoner() { -+ return summoner; -+ } -+ -+ public void setSummoner(@Nullable java.util.UUID summoner) { -+ this.summoner = summoner; -+ } -+ // Purpur end - Summoner API - @Override - protected PathNavigation createNavigation(Level world) { - FlyingPathNavigation navigationflying = new FlyingPathNavigation(this, world); -@@ -260,6 +271,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); - nbt.putInt("Invul", this.getInvulnerableTicks()); -+ if (getSummoner() != null) nbt.putUUID("Purpur.Summoner", getSummoner()); // Purpur - Summoner API - } - - @Override -@@ -269,6 +281,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { - if (this.hasCustomName()) { - this.bossEvent.setName(this.getDisplayName()); - } -+ if (nbt.contains("Purpur.Summoner")) setSummoner(nbt.getUUID("Purpur.Summoner")); // Purpur - Summoner API - - } - -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 22242d11e009acab4c9738a1c6ada8b9ba678a0c..49b7565c26ce8bf217ae60d233d5963331ce6696 100644 ---- a/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CarvedPumpkinBlock.java -@@ -72,7 +72,7 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock { - SnowGolem entitysnowman = (SnowGolem) EntityType.SNOW_GOLEM.create(world, EntitySpawnReason.TRIGGERED); - - 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 - Summoner API - } - } else { - BlockPattern.BlockPatternMatch shapedetector_shapedetectorcollection1 = this.getOrCreateIronGolemFull().find(world, pos); -@@ -82,7 +82,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 - Summoner API - } - } - } -@@ -90,6 +90,16 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock { - } - - private static void spawnGolemInWorld(Level world, BlockPattern.BlockPatternMatch patternResult, Entity entity, BlockPos pos) { -+ // Purpur start - Summoner API -+ 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 - Summoner API - // 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 0fbe66cc02bd3d95c0a5dcd55380a1b4a2f17ca6..11d6427682d8778d1cf668ee4c1d5760af2c185e 100644 ---- a/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/WitherSkullBlock.java -@@ -80,6 +80,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 - Summoner API - // 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 63cae1a2e95d8da17c45c4404a8dd0ca6a413c39..464a3713845548473a357ea66c6147b10ff2cb16 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 - 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/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java -index 4ce2373ff71c3c1b8951646e057587a3ab09e145..997b8e5059569de4ee8e70127c5d6019ce53afe3 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 - 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/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java -index 7881c6253c1d652c0c0d54a9a8accdf0a1ff0f3e..fe8be71121324f64346174922c7bc7f5d3a9de69 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 - 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/patches/server/0182-Customizable-sleeping-actionbar-messages.patch b/patches/server/0182-Customizable-sleeping-actionbar-messages.patch deleted file mode 100644 index 590ac6ea8d..0000000000 --- a/patches/server/0182-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 9ff7fb18e52bea06b02ebf0d1b97647df6b304dc..361885b28305e1c3e314b3b9f9807cf3c5ab843a 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1122,11 +1122,27 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - 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 4c69fa830a216c189f97ddfb62f0008b891455da..16fba60c063d059ee069821041d1ee55fe7cc62b 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1830,7 +1830,19 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - }); - - 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 e6079b44bd1364bd6be93d584634bab3553d5edb..01aad39f65bd9c38e470139b1b80a26f3af9c922 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -189,6 +189,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); -@@ -203,6 +206,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/0183-option-to-disable-shulker-box-items-from-dropping-co.patch b/patches/server/0183-option-to-disable-shulker-box-items-from-dropping-co.patch deleted file mode 100644 index a7072a07a6..0000000000 --- a/patches/server/0183-option-to-disable-shulker-box-items-from-dropping-co.patch +++ /dev/null @@ -1,39 +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 ac5dc472337cd9613db5fc03f64763245129dfd9..f8928d5ac2f107a904ecc636e6bdeee7edd8da45 100644 ---- a/src/main/java/net/minecraft/world/item/BlockItem.java -+++ b/src/main/java/net/minecraft/world/item/BlockItem.java -@@ -274,6 +274,7 @@ public class BlockItem extends Item { - ItemContainerContents itemcontainercontents = (ItemContainerContents) entity.getItem().set(DataComponents.CONTAINER, ItemContainerContents.EMPTY); - - if (itemcontainercontents != null) { -+ if (entity.level().purpurConfig.shulkerBoxItemDropContentsWhenDestroyed && this.getBlock() instanceof ShulkerBoxBlock) // Purpur - ItemUtils.onContainerDestroyed(entity, itemcontainercontents.nonEmptyItemsCopy()); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f277d8b257bf66552bfbd0ce98af2aa1e93779b7..f62de466c50287ecbe364f74bd715cbdf02808ea 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -215,6 +215,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 -> { -@@ -262,6 +263,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/0184-Big-dripleaf-tilt-delay.patch b/patches/server/0184-Big-dripleaf-tilt-delay.patch deleted file mode 100644 index 266e2b706a..0000000000 --- a/patches/server/0184-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 9e3f1441d62128535112621bf259c24f1a90595b..2535e6d71b690f8dfde41a7d9cb76b6f010f5aa7 100644 ---- a/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java -@@ -246,7 +246,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 f62de466c50287ecbe364f74bd715cbdf02808ea..20d4021d6222c58e97062756cf8c66b4f819d666 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -787,6 +787,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/0185-Player-ridable-in-water-option.patch b/patches/server/0185-Player-ridable-in-water-option.patch deleted file mode 100644 index 75dea03ca2..0000000000 --- a/patches/server/0185-Player-ridable-in-water-option.patch +++ /dev/null @@ -1,44 +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 ee88c493119a4a4629d6456c8698dd89e4f477f4..dda9391962b7360714cc1491e6cabfc2616dab25 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -2037,6 +2037,13 @@ public abstract class Player extends LivingEntity { - return slot != EquipmentSlot.BODY; - } - -+ // Purpur start -+ @Override -+ public boolean dismountsUnderwater() { -+ return !level().purpurConfig.playerRidableInWater; -+ } -+ // Purpur end -+ - 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 20d4021d6222c58e97062756cf8c66b4f819d666..363fba258c19fddb45b6bca459007def8c0da981 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -389,6 +389,7 @@ public class PurpurWorldConfig { - public double playerCriticalDamageMultiplier = 1.5D; - public int playerBurpDelay = 10; - public boolean playerBurpWhenFull = false; -+ public boolean playerRidableInWater = false; - private void playerSettings() { - if (PurpurConfig.version < 19) { - boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer); -@@ -411,6 +412,7 @@ 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); -+ playerRidableInWater = getBoolean("gameplay-mechanics.player.ridable-in-water", playerRidableInWater); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0186-Config-to-disable-Enderman-teleport-on-projectile-hi.patch b/patches/server/0186-Config-to-disable-Enderman-teleport-on-projectile-hi.patch deleted file mode 100644 index 03f8045a75..0000000000 --- a/patches/server/0186-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 efeb8a214c3e0ab6cb28a6f6e0d6417916ed670f..327686f0c47894259532a15e98d69d332a6d483a 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -424,6 +424,7 @@ public class EnderMan extends Monster implements NeutralMob { - } else { - flag1 = flag && this.hurtWithCleanWater(world, source, (ThrownPotion) source.getDirectEntity(), amount); - -+ if (!flag1 && world.purpurConfig.endermanIgnoreProjectiles) return super.hurtServer(world, source, amount); // Purpur - if (this.tryEscape(com.destroystokyo.paper.event.entity.EndermanEscapeEvent.Reason.INDIRECT)) { // Paper - EndermanEscapeEvent - 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 363fba258c19fddb45b6bca459007def8c0da981..7463320f50e863b6b8c5ba6127527b2b1fff72f6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1464,6 +1464,7 @@ public class PurpurWorldConfig { - public boolean endermanAggroEndermites = true; - public boolean endermanAggroEndermitesOnlyIfPlayerSpawned = 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); -@@ -1486,6 +1487,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean endermiteRidable = false; diff --git a/patches/server/0187-Add-compass-command.patch b/patches/server/0187-Add-compass-command.patch deleted file mode 100644 index 346631f880..0000000000 --- a/patches/server/0187-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 357f2b078a1bd4730599d6d3f5ac3c6f859d3d86..5873f74df67d59f25682286a5b027963d8a6d382 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -260,6 +260,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 16fba60c063d059ee069821041d1ee55fe7cc62b..ad4ace81452d83cc01ac9de5ef323770a8a2f417 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -329,6 +329,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - public org.bukkit.event.player.PlayerQuitEvent.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 - public boolean purpurClient = false; // Purpur - Purpur client support - private boolean tpsBar = false; // Purpur -+ private boolean compassBar = false; // Purpur - - // Paper start - rewrite chunk system - private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader; -@@ -692,6 +693,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - } - - 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 -@@ -745,6 +747,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - - this.saveEnderPearls(nbt); - nbt.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur -+ nbt.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur - } - - private void saveParentVehicle(CompoundTag nbt) { -@@ -3445,5 +3448,13 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - 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 01aad39f65bd9c38e470139b1b80a26f3af9c922..639b534982547beede11654edc2ed719c66709b6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -258,6 +258,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 = ""; -@@ -280,6 +285,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 7463320f50e863b6b8c5ba6127527b2b1fff72f6..c541cba787c4a07786a4d800f167a70e287c7d9e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -216,6 +216,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 -> { -@@ -264,6 +265,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 6796fd6a936212a6eb768d9cf0fa5e74132c89e8..65c1bac3daf781c66442350ba08c43b21d2f1637 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/0188-Toggle-for-kinetic-damage.patch b/patches/server/0188-Toggle-for-kinetic-damage.patch deleted file mode 100644 index 40e8a303d5..0000000000 --- a/patches/server/0188-Toggle-for-kinetic-damage.patch +++ /dev/null @@ -1,36 +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 ecf8e9742951c144170ce818c14d368011ef99a8..c7e2330e7f30081dbf5d79f08c8adb1d7d84fa03 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -3212,6 +3212,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - - if (f > 0.0F) { - this.playSound(this.getFallDamageSound((int) f), 1.0F, 1.0F); -+ if (level().purpurConfig.elytraKineticDamage) // Purpur - this.hurt(this.damageSources().flyIntoWall(), f); - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index c541cba787c4a07786a4d800f167a70e287c7d9e..3837d3857f92bf90d2e5abbd0ccbecff7c796e4e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -182,11 +182,13 @@ public class PurpurWorldConfig { - 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; diff --git a/patches/server/0189-Add-Option-for-disable-observer-clocks.patch b/patches/server/0189-Add-Option-for-disable-observer-clocks.patch deleted file mode 100644 index 7a58123ef9..0000000000 --- a/patches/server/0189-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 93ed9406c34804831b86d006dbd6087db9948f08..26cb9990b91991e0a2eadc2dcbbf229e2e88fb2d 100644 ---- a/src/main/java/net/minecraft/world/level/block/ObserverBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/ObserverBlock.java -@@ -75,6 +75,7 @@ public class ObserverBlock extends DirectionalBlock { - @Override - protected BlockState updateShape(BlockState state, LevelReader world, ScheduledTickAccess tickView, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random) { - if (state.getValue(ObserverBlock.FACING) == direction && !(Boolean) state.getValue(ObserverBlock.POWERED)) { -+ if (!world.getWorldBorder().world.purpurConfig.disableObserverClocks || !(neighborState.getBlock() instanceof ObserverBlock) || neighborState.getValue(ObserverBlock.FACING).getOpposite() != direction) // Purpur - this.startSignal(world, tickView, pos); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 3837d3857f92bf90d2e5abbd0ccbecff7c796e4e..683ae285004e1a6e3f58e0c2c16d5cb31f56a0d8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -364,6 +364,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/0190-Customizeable-Zombie-Villager-curing-times.patch b/patches/server/0190-Customizeable-Zombie-Villager-curing-times.patch deleted file mode 100644 index 7f0e0f474c..0000000000 --- a/patches/server/0190-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 3f3e0dd479afad1ca73ad9f76ed673fa399955c6..b10a12d9ddec3c064f5c42d6d8783e6608c917e0 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -230,7 +230,7 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - if (this.hasEffect(MobEffects.WEAKNESS)) { - itemstack.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 - Customizeable Zombie Villager curing times - } - - return InteractionResult.SUCCESS_SERVER; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 683ae285004e1a6e3f58e0c2c16d5cb31f56a0d8..ece185b07394b2a09b322b3e00cd78076097b43a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2970,6 +2970,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); -@@ -2986,6 +2988,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/0191-Option-for-sponges-to-work-on-lava-and-mud.patch b/patches/server/0191-Option-for-sponges-to-work-on-lava-and-mud.patch deleted file mode 100644 index 0503e6d715..0000000000 --- a/patches/server/0191-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 0edb7e821a60fe95fea3cae900e5b88192946fe6..295c1439645c8da7f84f0a72abb3235ee879e89c 100644 ---- a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java -@@ -80,7 +80,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 BlockPos.TraversalNodeStatus.SKIP; - } else { - Block block = iblockdata.getBlock(); -@@ -95,6 +95,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 BlockPos.TraversalNodeStatus.SKIP; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index ece185b07394b2a09b322b3e00cd78076097b43a..1385065c45b817c4677010b9a4968f0987818f0a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -973,9 +973,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 = false; diff --git a/patches/server/0192-Toggle-for-Wither-s-spawn-sound.patch b/patches/server/0192-Toggle-for-Wither-s-spawn-sound.patch deleted file mode 100644 index 892d480f4e..0000000000 --- a/patches/server/0192-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 5f40d6d5b002f1f098245c3aa1174c85759191ce..9e37864a630151aee431aa4031ecc4033de1766f 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 -@@ -425,7 +425,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { - } - // CraftBukkit end - -- if (!this.isSilent()) { -+ if (!this.isSilent() && level().purpurConfig.witherPlaySpawnSound) { - // CraftBukkit start - Use relative location for far away sounds - // worldserver.globalLevelEvent(1023, new BlockPosition(this), 0); - int viewDistance = world.getCraftServer().getViewDistance() * 16; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1385065c45b817c4677010b9a4968f0987818f0a..71531ec956bf1de473bca192988a26a0a33b0458 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2805,6 +2805,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); -@@ -2827,6 +2828,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/0193-Cactus-breaks-from-solid-neighbors-config.patch b/patches/server/0193-Cactus-breaks-from-solid-neighbors-config.patch deleted file mode 100644 index f01df3a3ac..0000000000 --- a/patches/server/0193-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 c045b1cccf0047dbef8c04d5a28d31d53389054f..bbfd8f5d404d0add94f0d8ac89a2964692b37e44 100644 ---- a/src/main/java/net/minecraft/world/level/block/CactusBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CactusBlock.java -@@ -115,7 +115,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 71531ec956bf1de473bca192988a26a0a33b0458..84c01175dd4d65b1cb8b185f03d93a4cea0e0656 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -814,6 +814,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/0194-Config-to-remove-curse-of-binding-with-weakness.patch b/patches/server/0194-Config-to-remove-curse-of-binding-with-weakness.patch deleted file mode 100644 index 14da388ac0..0000000000 --- a/patches/server/0194-Config-to-remove-curse-of-binding-with-weakness.patch +++ /dev/null @@ -1,39 +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/ArmorSlot.java b/src/main/java/net/minecraft/world/inventory/ArmorSlot.java -index db7caaa2e77b9b98adac8add3040131c673c036b..262d9b2507d37edf0ed9c0821059e518d1baa9e9 100644 ---- a/src/main/java/net/minecraft/world/inventory/ArmorSlot.java -+++ b/src/main/java/net/minecraft/world/inventory/ArmorSlot.java -@@ -44,7 +44,7 @@ class ArmorSlot extends Slot { - @Override - public boolean mayPickup(Player playerEntity) { - ItemStack itemStack = this.getItem(); -- return (itemStack.isEmpty() || playerEntity.isCreative() || !EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE)) -+ return (itemStack.isEmpty() || playerEntity.isCreative() || (!EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE) || playerEntity.level().purpurConfig.playerRemoveBindingWithWeakness && playerEntity.hasEffect(net.minecraft.world.effect.MobEffects.WEAKNESS))) - && super.mayPickup(playerEntity); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 84c01175dd4d65b1cb8b185f03d93a4cea0e0656..8f90dbc4395381f712befbbf09b6ae94887843e7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -399,6 +399,7 @@ public class PurpurWorldConfig { - public int playerBurpDelay = 10; - public boolean playerBurpWhenFull = false; - 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); -@@ -422,6 +423,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0195-Conduit-behavior-configuration.patch b/patches/server/0195-Conduit-behavior-configuration.patch deleted file mode 100644 index 324d8e4025..0000000000 --- a/patches/server/0195-Conduit-behavior-configuration.patch +++ /dev/null @@ -1,130 +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 d354c2ad41533ec8d52f7c5f63f8f74a0b1ce235..a43c41a26c1e34a2f4eda1c498a562664a783c77 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 -@@ -169,7 +169,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) { -@@ -189,13 +189,13 @@ public class ConduitBlockEntity extends BlockEntity { - - private static void applyEffects(Level world, BlockPos pos, List activatingBlocks) { - // CraftBukkit start -- ConduitBlockEntity.applyEffects(world, pos, ConduitBlockEntity.getRange(activatingBlocks)); -+ ConduitBlockEntity.applyEffects(world, pos, ConduitBlockEntity.getRange(activatingBlocks, world)); // Purpur - } - -- public static int getRange(List list) { -+ public static int getRange(List list, Level world) { // Purpur - // CraftBukkit end - int i = list.size(); -- int j = i / 7 * 16; -+ int j = i / 7 * world.purpurConfig.conduitDistance; // Purpur - // CraftBukkit start - return j; - } -@@ -238,20 +238,20 @@ public class ConduitBlockEntity extends BlockEntity { - tileentityconduit.destroyTarget = ConduitBlockEntity.findDestroyTarget(world, blockposition, tileentityconduit.destroyTargetUUID); - tileentityconduit.destroyTargetUUID = null; - } else if (tileentityconduit.destroyTarget == null) { -- List list1 = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(blockposition), (entityliving1) -> { -+ List list1 = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(blockposition, world), (entityliving1) -> { // Purpur - return entityliving1 instanceof Enemy && entityliving1.isInWaterOrRain(); - }); - - if (!list1.isEmpty()) { - tileentityconduit.destroyTarget = (LivingEntity) list1.get(world.random.nextInt(list1.size())); - } -- } else if (!tileentityconduit.destroyTarget.isAlive() || !blockposition.closerThan(tileentityconduit.destroyTarget.blockPosition(), 8.0D)) { -+ } else if (!tileentityconduit.destroyTarget.isAlive() || !blockposition.closerThan(tileentityconduit.destroyTarget.blockPosition(), world.purpurConfig.conduitDamageDistance)) { // Purpur - tileentityconduit.destroyTarget = null; - } - - // CraftBukkit start - if (damageTarget && tileentityconduit.destroyTarget != null) { -- if (tileentityconduit.destroyTarget.hurtServer((ServerLevel) world, world.damageSources().magic().directBlock(world, blockposition), 4.0F)) { -+ if (tileentityconduit.destroyTarget.hurtServer((ServerLevel) world, world.damageSources().magic().directBlock(world, blockposition), world.purpurConfig.conduitDamageAmount)) { // Purpur - world.playSound(null, tileentityconduit.destroyTarget.getX(), tileentityconduit.destroyTarget.getY(), tileentityconduit.destroyTarget.getZ(), SoundEvents.CONDUIT_ATTACK_TARGET, SoundSource.BLOCKS, 1.0F, 1.0F); - } - // CraftBukkit end -@@ -276,16 +276,22 @@ public class ConduitBlockEntity extends BlockEntity { - } - - public 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/bukkit/craftbukkit/block/CraftConduit.java b/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java -index c1759aeb3e6ad0e4eb66cba3da1b120dd1dce812..1a91bc2e422db0eba65694ac046f1b362c6b0cd6 100644 ---- a/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java -+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java -@@ -73,7 +73,7 @@ public class CraftConduit extends CraftBlockEntityState impl - public int getRange() { - this.ensureNoWorldGeneration(); - ConduitBlockEntity conduit = (ConduitBlockEntity) this.getTileEntityFromWorld(); -- return (conduit != null) ? ConduitBlockEntity.getRange(conduit.effectBlocks) : 0; -+ return (conduit != null) ? ConduitBlockEntity.getRange(conduit.effectBlocks, this.world.getHandle()) : 0; // Purpur - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 8f90dbc4395381f712befbbf09b6ae94887843e7..df054975a396856693eb6e70637a50b089836e37 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -3039,4 +3039,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.getValue(ResourceLocation.parse(key.toString())); -+ if (!block.defaultBlockState().isAir()) { -+ conduitBlockList.add(block); -+ } -+ }); -+ conduitBlocks = conduitBlockList.toArray(Block[]::new); -+ } - } diff --git a/patches/server/0196-Cauldron-fill-chances.patch b/patches/server/0196-Cauldron-fill-chances.patch deleted file mode 100644 index e770696273..0000000000 --- a/patches/server/0196-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 c9968934f4ecaa8d81e545f279b3001c7b1ce545..03e4fce6f8226451365fc2831b5bf1e5e6091730 100644 ---- a/src/main/java/net/minecraft/world/level/block/CauldronBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CauldronBlock.java -@@ -37,7 +37,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 53cea36ec931de89e0060613acf87beb51dc16ec..fd5489993dca0f940da69e9163f78e5c2e6ee063 100644 ---- a/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java -@@ -194,7 +194,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); - -@@ -203,13 +203,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 df054975a396856693eb6e70637a50b089836e37..5f97cbbe75ffd6a75cb39db7fc24c61e26068cf8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -3062,4 +3062,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/0197-Config-to-allow-mobs-to-pathfind-over-rails.patch b/patches/server/0197-Config-to-allow-mobs-to-pathfind-over-rails.patch deleted file mode 100644 index fafa33da0b..0000000000 --- a/patches/server/0197-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 c84fd369d92932903c76bb2012602617d3e2d213..e295fe4aac742ff8942b23456fdce8d7ff944e90 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -@@ -240,7 +240,7 @@ public class WalkNodeEvaluator extends NodeEvaluator { - if ((node == null || node.costMalus < 0.0F) - && maxYStep > 0 - && (pathType != PathType.FENCE || this.canWalkOverFences()) -- && pathType != PathType.UNPASSABLE_RAIL -+ && (this.mob.level().purpurConfig.mobsIgnoreRails || pathType != PathType.UNPASSABLE_RAIL) // Purpur - Config to allow mobs to pathfind over rails - && pathType != PathType.TRAPDOOR - && pathType != PathType.POWDER_SNOW) { - node = this.tryJumpOn(x, y, z, maxYStep, prevFeetY, direction, nodeType, mutableBlockPos); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 5f97cbbe75ffd6a75cb39db7fc24c61e26068cf8..ff4d9ccbb9801649ad8ea9dac72b0f32f58e06c1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -139,6 +139,7 @@ public class PurpurWorldConfig { - public boolean imposeTeleportRestrictionsOnEndPortals = false; - public boolean tickFluids = true; - public double mobsBlindnessMultiplier = 1; -+ public boolean mobsIgnoreRails = false; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative); -@@ -160,6 +161,7 @@ public class PurpurWorldConfig { - 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); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0198-Shulker-change-color-with-dye.patch b/patches/server/0198-Shulker-change-color-with-dye.patch deleted file mode 100644 index 9637a15e8f..0000000000 --- a/patches/server/0198-Shulker-change-color-with-dye.patch +++ /dev/null @@ -1,50 +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 1957e2be5087e7bf85be5dfba53de8385dbeadd6..2052549f2e6b23aff5491bb0cc1af00b7f560227 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -126,6 +126,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 1240df9368855f836412b06cf564926a18bfe90d..248e2fb41df3cb1efc64921074a51a02419d8fbb 100644 ---- a/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/AbstractFurnaceMenu.java -@@ -114,7 +114,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 - Added the ability to add combustible items -+ if (this.isFuel(itemstack1)) { -+ if (!this.moveItemStackTo(itemstack1, 1, 2, false)) { -+ return ItemStack.EMPTY; -+ } -+ } -+ // Purpur end - Added the ability to add combustible items - } - } else if (this.isFuel(itemstack1)) { - if (!this.moveItemStackTo(itemstack1, 1, 2, false)) { -diff --git a/src/main/java/net/minecraft/world/level/block/entity/FuelValues.java b/src/main/java/net/minecraft/world/level/block/entity/FuelValues.java -index 61ef08ac941b1e8988d001241780d3a1582f7a2d..6ceb938d6f6a4f2bc5b786c7af7bd291f7486340 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/FuelValues.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/FuelValues.java -@@ -17,7 +17,7 @@ import net.minecraft.world.level.ItemLike; - import net.minecraft.world.level.block.Blocks; - - public class FuelValues { -- private final Object2IntSortedMap values; -+ public final Object2IntSortedMap values; // Purpur - private -> public - Added the ability to add combustible items - - FuelValues(Object2IntSortedMap fuelValues) { - this.values = fuelValues; -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 3b5225c3ba8e10df45df2fbf2305f8542b2f1f39..9aa6744ab3a19e1ecf32b4aa059b7f4c555f03ff 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1629,6 +1629,22 @@ public final class CraftServer implements Server { - 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 -+ - @Override - public List getRecipesFor(ItemStack result) { - Preconditions.checkArgument(result != null, "ItemStack cannot be null"); diff --git a/patches/server/0201-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch b/patches/server/0201-Option-for-if-rain-and-thunder-should-stop-on-sleep.patch deleted file mode 100644 index 738a826301..0000000000 --- a/patches/server/0201-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 361885b28305e1c3e314b3b9f9807cf3c5ab843a..0487d55861302e1bf84225901d873b02c2f11d6e 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1282,6 +1282,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - @VisibleForTesting - public void resetWeatherCycle() { - // CraftBukkit start -+ if (this.purpurConfig.rainStopsAfterSleep) // Purpur - 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.... -@@ -1289,6 +1290,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - this.serverLevelData.setRainTime(0); - } - // CraftBukkit end -+ if (this.purpurConfig.thunderStopsAfterSleep) // Purpur - 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. -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a47ddcb40340c19ce99a8befe8034240e0457acb..e5934c276e286820944488e2d629b01d58dbf200 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -140,6 +140,8 @@ public class PurpurWorldConfig { - public boolean tickFluids = true; - public double mobsBlindnessMultiplier = 1; - 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); -@@ -162,6 +164,8 @@ public class PurpurWorldConfig { - 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); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0202-Chance-for-azalea-blocks-to-grow-into-trees-naturall.patch b/patches/server/0202-Chance-for-azalea-blocks-to-grow-into-trees-naturall.patch deleted file mode 100644 index 125b134d54..0000000000 --- a/patches/server/0202-Chance-for-azalea-blocks-to-grow-into-trees-naturall.patch +++ /dev/null @@ -1,79 +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 affbbf6abc6bc09ecb652c1dee92aa297458bc39..c58e07f2a99e3cbb5bd5d3693c006919e0710b7a 100644 ---- a/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/AzaleaBlock.java -@@ -50,6 +50,20 @@ public class AzaleaBlock extends BushBlock implements BonemealableBlock { - - @Override - public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) { -+ // Purpur start - Chance for azalea blocks to grow into trees naturally -+ 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 - Chance for azalea blocks to grow into trees naturally - TreeGrower.AZALEA.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 9bafac9b2c7b487f745fb64354f4bbc6a7ded468..c5f8227cd9631d98cc8404e3f6d6109a55c617aa 100644 ---- a/src/main/java/net/minecraft/world/level/block/Blocks.java -+++ b/src/main/java/net/minecraft/world/level/block/Blocks.java -@@ -6454,6 +6454,7 @@ public class Blocks { - BlockBehaviour.Properties.of() - .mapColor(MapColor.PLANT) - .forceSolidOff() -+ .randomTicks() // Purpur - Chance for azalea blocks to grow into trees naturally - .instabreak() - .sound(SoundType.AZALEA) - .noOcclusion() -@@ -6465,6 +6466,7 @@ public class Blocks { - 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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index e5934c276e286820944488e2d629b01d58dbf200..c44be697e59049adb50967b6d91016035fb69956 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -774,6 +774,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; -@@ -911,6 +916,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/0203-Shift-right-click-to-use-exp-for-mending.patch b/patches/server/0203-Shift-right-click-to-use-exp-for-mending.patch deleted file mode 100644 index 15f0e872fc..0000000000 --- a/patches/server/0203-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 7315e604a2f9cb068eb5bbca744e44eeabac09c9..88eb3774f688bcff383efa7f113bd0b1b97d8a11 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java -@@ -524,6 +524,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); - boolean cancelledBlock = false; -@@ -632,4 +633,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 ea6713bd5a2807cf00d664296c13e076b2fca3a0..47952e504cc4d36b1d875651bb9bbd0a90606ae2 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2155,6 +2155,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - - 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 c44be697e59049adb50967b6d91016035fb69956..382ce37738d278400b609af7fb9189c93b907eeb 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -406,6 +406,7 @@ public class PurpurWorldConfig { - public boolean playerBurpWhenFull = false; - 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); -@@ -430,6 +431,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0204-Dolphins-naturally-aggressive-to-players-chance.patch b/patches/server/0204-Dolphins-naturally-aggressive-to-players-chance.patch deleted file mode 100644 index 20e535735f..0000000000 --- a/patches/server/0204-Dolphins-naturally-aggressive-to-players-chance.patch +++ /dev/null @@ -1,68 +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 46862ebf302454a077a837da001abdceeffa1026..64d1e932ac3bc673cffa324d8e75e8da596d6941 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -86,6 +86,7 @@ public class Dolphin extends AgeableWaterCreature { - }; - public static final float BABY_SCALE = 0.65F; - private int spitCooldown; // Purpur - Ridables -+ private boolean isNaturallyAggressiveToPlayers; // Purpur - Dolphins naturally aggressive to players chance - - public Dolphin(EntityType type, Level world) { - super(type, world); -@@ -182,6 +183,7 @@ public class Dolphin extends AgeableWaterCreature { - SpawnGroupData groupdataentity1 = (SpawnGroupData) Objects.requireNonNullElseGet(entityData, () -> { - return new AgeableMob.AgeableMobGroupData(0.1F); - }); -+ this.isNaturallyAggressiveToPlayers = world.getLevel().purpurConfig.dolphinNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.dolphinNaturallyAggressiveToPlayersChance; // Purpur - Dolphins naturally aggressive to players chance - - return super.finalizeSpawn(world, difficulty, spawnReason, groupdataentity1); - } -@@ -259,18 +261,20 @@ public class Dolphin extends AgeableWaterCreature { - 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 - Ridables -+ 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.0D)); - this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0D, 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.2000000476837158D, true)); -+ //this.goalSelector.addGoal(6, new MeleeAttackGoal(this, 1.2000000476837158D, 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.0D, 1.0D)); - this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables - 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, (ignored, ignored2) -> isNaturallyAggressiveToPlayers)); // Purpur - Dolphins naturally aggressive to players chance - } - - 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 382ce37738d278400b609af7fb9189c93b907eeb..b1a18e3e1b2459adda1a2621a0aa7219d5580a0f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1367,6 +1367,7 @@ public class PurpurWorldConfig { - public double dolphinScale = 1.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); -@@ -1382,6 +1383,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean donkeyRidableInWater = false; diff --git a/patches/server/0205-Cows-naturally-aggressive-to-players-chance.patch b/patches/server/0205-Cows-naturally-aggressive-to-players-chance.patch deleted file mode 100644 index 85959633f5..0000000000 --- a/patches/server/0205-Cows-naturally-aggressive-to-players-chance.patch +++ /dev/null @@ -1,90 +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 4d5d4f7bb24aded2933daa003b4c4bc38601e12d..42324d0d22c1f1a233aa6e6a810171b26894f8bc 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -37,6 +37,7 @@ import org.bukkit.event.player.PlayerBucketFillEvent; - // CraftBukkit end - - public class Cow extends Animal { -+ private boolean isNaturallyAggressiveToPlayers; // Purpur - - private static final EntityDimensions BABY_DIMENSIONS = EntityType.COW.getDimensions().scale(0.5F).withEyeHeight(0.665F); - -@@ -66,6 +67,7 @@ public class Cow extends Animal { - public void initAttributes() { - this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.cowMaxHealth); - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.cowScale); -+ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.cowNaturallyAggressiveToPlayersDamage); // Purpur - } - // Purpur end - Configurable entity base attributes - // Purpur start - Make entity breeding times configurable -@@ -80,11 +82,18 @@ public class Cow extends Animal { - return this.level().purpurConfig.cowTakeDamageFromWater; - } - // Purpur end - Toggle for water sensitive mob damage -+ @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.SpawnGroupData entityData) { -+ this.isNaturallyAggressiveToPlayers = world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance; -+ return super.finalizeSpawn(world, difficulty, spawnReason, entityData); -+ } -+ - @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.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)); - this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, (itemstack) -> { - return 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); // Purpur - Cows eat mushrooms -@@ -93,6 +102,7 @@ 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, (ignored, ignored2) -> isNaturallyAggressiveToPlayers)); // Purpur - } - - @Override -@@ -101,7 +111,7 @@ public class Cow extends Animal { - } - - public static AttributeSupplier.Builder createAttributes() { -- return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.MOVEMENT_SPEED, 0.20000000298023224D); -+ return Animal.createAnimalAttributes().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 b1a18e3e1b2459adda1a2621a0aa7219d5580a0f..f5b04a904118e2ef15a2b735eca9b3e6adbbf63b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1299,7 +1299,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); -@@ -1313,6 +1320,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 creakingRidable = false; diff --git a/patches/server/0206-Option-for-beds-to-explode-on-villager-sleep.patch b/patches/server/0206-Option-for-beds-to-explode-on-villager-sleep.patch deleted file mode 100644 index 190da8867a..0000000000 --- a/patches/server/0206-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 0bca1eeaa0f35d702dad41758c72bb3c9ab35220..c912268081beda34c472cff13e41c9dfd51902f2 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -1080,6 +1080,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 f5b04a904118e2ef15a2b735eca9b3e6adbbf63b..bb0635a71dd3beca33d644b365df8133cb2b2a3a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -793,6 +793,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; -@@ -803,6 +804,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/0207-Halloween-options-and-optimizations.patch b/patches/server/0207-Halloween-options-and-optimizations.patch deleted file mode 100644 index 556e882dae..0000000000 --- a/patches/server/0207-Halloween-options-and-optimizations.patch +++ /dev/null @@ -1,83 +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 79175bd42f37f10fae812e101dd5b09b209ebe30..7b647f41bd5ca4fa62795ad2fbcb5b73a3405979 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -307,7 +307,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; -@@ -317,6 +317,11 @@ 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; -+ public static boolean isHalloweenSeason(Level level) { return level.purpurConfig.forceHalloweenSeason || isHalloween(); } // Purpur - private static boolean isHalloween() { - LocalDate localdate = LocalDate.now(); - int i = localdate.get(ChronoField.DAY_OF_MONTH); -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 27bdd3c4e0dc3fbb906689e2390c945bf3d40eea..20252b22c88d4cbfe7700052a8c56c9e0d703752 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -@@ -140,11 +140,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 - Add world settings for mobs picking up loot - 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 d0fcd51e36e7c7e774fcf9b1db42ec7fceb9fc41..37ee837f6e722cd4cb07ae9fcd366a1473dc0b49 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -598,11 +598,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 bb0635a71dd3beca33d644b365df8133cb2b2a3a..91115a2c66fc34654de9a30764450fdf7d06006a 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1730,6 +1730,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/0208-Config-for-grindstones.patch b/patches/server/0208-Config-for-grindstones.patch deleted file mode 100644 index c697acf1da..0000000000 --- a/patches/server/0208-Config-for-grindstones.patch +++ /dev/null @@ -1,160 +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 5687f492fc76f699e2a388790ca5380d9b8c8d0a..cc229f3e1e9527cbedf929e326731943bb513dae 100644 ---- a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java -@@ -135,7 +135,7 @@ public class GrindstoneMenu extends AbstractContainerMenu { - Holder holder = (Holder) entry.getKey(); - int k = entry.getIntValue(); - -- if (!holder.is(EnchantmentTags.CURSE)) { -+ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(holder.value())) { // Purpur - j += ((Enchantment) holder.value()).getMinCost(k); - } - } -@@ -222,7 +222,7 @@ public class GrindstoneMenu extends AbstractContainerMenu { - Entry> entry = (Entry) iterator.next(); - Holder holder = (Holder) entry.getKey(); - -- if (!holder.is(EnchantmentTags.CURSE) || itemenchantments_a.getLevel(holder) == 0) { -+ if (!org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(holder.value()) || itemenchantments_a.getLevel(holder) == 0) { // Purpur - itemenchantments_a.upgrade(holder, entry.getIntValue()); - } - } -@@ -230,10 +230,70 @@ public class GrindstoneMenu extends AbstractContainerMenu { - }); - } - -+ // Purpur start -+ 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 - private ItemStack removeNonCursesFrom(ItemStack item) { - ItemEnchantments itemenchantments = EnchantmentHelper.updateEnchantments(item, (itemenchantments_a) -> { - itemenchantments_a.removeIf((holder) -> { -- return !holder.is(EnchantmentTags.CURSE); -+ return !org.purpurmc.purpur.PurpurConfig.grindstoneIgnoredEnchants.contains(holder.value()); // Purpur - }); - }); - -@@ -248,6 +308,23 @@ public class GrindstoneMenu extends AbstractContainerMenu { - } - - item.set(DataComponents.REPAIR_COST, i); -+ -+ // Purpur start -+ 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 -+ - return item; - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 639b534982547beede11654edc2ed719c66709b6..82903b6ce4cf30b2d95001455ee4e3a454b3ddd5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -312,6 +312,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); -@@ -346,6 +349,21 @@ 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 -> { -+ Registry registry = MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.ENCHANTMENT); -+ Enchantment enchantment = registry.getValue(ResourceLocation.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); - } - - public static boolean allowInapplicableEnchants = false; diff --git a/patches/server/0209-UPnP-Port-Forwarding.patch b/patches/server/0209-UPnP-Port-Forwarding.patch deleted file mode 100644 index e26abf20e7..0000000000 --- a/patches/server/0209-UPnP-Port-Forwarding.patch +++ /dev/null @@ -1,83 +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 9219b31a7273b08e7acd1a953c260a5520333922..80a8bd2dc32763f8ee2062c2d1b36188f2532523 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -331,6 +331,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping - public boolean lagging = false; // Purpur - Lagging threshold -+ protected boolean upnp = false; // Purpur - UPnP Port Forwarding - - public volatile Thread shutdownThread; // Paper - public volatile boolean abnormalExit = false; // Paper -@@ -1057,6 +1058,15 @@ 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 1b94f26e78db062f80d806b82f714a815b4710ff..b6ba6ebe6ac15cbcb5d3a6221b47762e37c4a56f 100644 ---- a/src/main/java/net/minecraft/world/level/block/CampfireBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CampfireBlock.java -@@ -140,7 +140,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 - Campfire option for lit when placed - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 91115a2c66fc34654de9a30764450fdf7d06006a..eda31d2f341fd800e9c4d84c8b2c0207746ae227 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -836,6 +836,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/0211-options-to-extinguish-fire-blocks-with-snowballs.patch b/patches/server/0211-options-to-extinguish-fire-blocks-with-snowballs.patch deleted file mode 100644 index 9fd3ecd46a..0000000000 --- a/patches/server/0211-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 f712963fcd80535eee2bd04ec55ae1abdadef2bd..6fdacf2f6934521a0dd4b25aea35a6a14123da0a 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -@@ -63,6 +63,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 eda31d2f341fd800e9c4d84c8b2c0207746ae227..d37cc14aa820be534d0afb434cd56a29764f9a6f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -225,6 +225,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 -> { -@@ -274,6 +277,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/0212-Add-option-to-disable-zombie-villagers-cure.patch b/patches/server/0212-Add-option-to-disable-zombie-villagers-cure.patch deleted file mode 100644 index 03656a547a..0000000000 --- a/patches/server/0212-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 b10a12d9ddec3c064f5c42d6d8783e6608c917e0..40490767e474bc1e954c8f837f25268d2fd38f5b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -227,7 +227,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 - itemstack.consume(1, player); - if (!this.level().isClientSide) { - this.startConverting(player.getUUID(), this.random.nextInt(level().purpurConfig.zombieVillagerCuringTimeMax - level().purpurConfig.zombieVillagerCuringTimeMin + 1) + level().purpurConfig.zombieVillagerCuringTimeMin); // Purpur - Customizeable Zombie Villager curing times -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d37cc14aa820be534d0afb434cd56a29764f9a6f..12dc84361b78901dfb3725ff3add422678c19ea8 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -3036,6 +3036,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); -@@ -3054,6 +3055,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/0213-Persistent-BlockEntity-Lore-and-DisplayName.patch b/patches/server/0213-Persistent-BlockEntity-Lore-and-DisplayName.patch deleted file mode 100644 index 4f940870d4..0000000000 --- a/patches/server/0213-Persistent-BlockEntity-Lore-and-DisplayName.patch +++ /dev/null @@ -1,164 +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 f8928d5ac2f107a904ecc636e6bdeee7edd8da45..3d7a09c81b10f7a34d55670b7f2cc50b80550380 100644 ---- a/src/main/java/net/minecraft/world/item/BlockItem.java -+++ b/src/main/java/net/minecraft/world/item/BlockItem.java -@@ -151,7 +151,16 @@ 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.persistentTileEntityLore) { -+ BlockEntity blockEntity1 = world.getBlockEntity(pos); -+ if (blockEntity1 != null) { -+ blockEntity1.setPersistentLore(stack.getOrDefault(DataComponents.LORE, net.minecraft.world.item.component.ItemLore.EMPTY)); -+ } -+ } -+ 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 b88326cc6c54bf38a37f2491bfb2f0e0deb2b1df..7531381e2e4d87ceeaa6478e13b23f6c072bef07 100644 ---- a/src/main/java/net/minecraft/world/level/block/Block.java -+++ b/src/main/java/net/minecraft/world/level/block/Block.java -@@ -303,7 +303,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, applyLoreFromTile(itemstack, blockEntity)); // Purpur - }); - state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true); - } -@@ -322,7 +322,7 @@ public class Block extends BlockBehaviour implements ItemLike { - 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 - } - state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping - block.popExperience(serverLevel, pos, event.getExpToDrop()); // Paper - Properly handle xp dropping -@@ -339,13 +339,32 @@ public class Block extends BlockBehaviour implements ItemLike { - // Paper end - Properly handle xp dropping - if (world instanceof ServerLevel) { - Block.getDrops(state, (ServerLevel) world, pos, blockEntity, entity, tool).forEach((itemstack1) -> { -- Block.popResource(world, pos, itemstack1); -+ Block.popResource(world, pos, applyLoreFromTile(itemstack1, blockEntity)); // Purpur - }); - state.spawnAfterBreak((ServerLevel) world, pos, tool, dropExperience); // Paper - Properly handle xp dropping - } - - } - -+ // Purpur start -+ 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 -+ - 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 1f664c10138a6e19bdc0051fa80575516d5602e7..5c5cc77ff2e050e80dc9f6f62ede68d177a0015f 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 -@@ -94,6 +94,12 @@ public abstract class BlockEntity { - if (persistentDataTag instanceof CompoundTag) { - this.persistentDataContainer.putAll((CompoundTag) persistentDataTag); - } -+ // Purpur start -+ if (nbt.contains("Purpur.persistentLore")) { -+ net.minecraft.world.item.component.ItemLore.CODEC.decode(net.minecraft.nbt.NbtOps.INSTANCE, nbt.getCompound("Purpur.persistentLore")).result() -+ .ifPresent(tag -> this.persistentLore = tag.getFirst()); -+ } -+ // Purpur end - } - // CraftBukkit end - -@@ -110,6 +116,15 @@ public abstract class BlockEntity { - this.loadAdditional(nbt, registries); - } - -+ // Purpur start -+ protected void saveAdditional(CompoundTag nbt) { -+ if (this.persistentLore != null) { -+ net.minecraft.world.item.component.ItemLore.CODEC.encodeStart(net.minecraft.nbt.NbtOps.INSTANCE, this.persistentLore).result() -+ .ifPresent(tag -> nbt.put("Purpur.persistentLore", tag)); -+ } -+ } -+ // Purpur end -+ - protected void saveAdditional(CompoundTag nbt, HolderLookup.Provider registries) {} - - public final CompoundTag saveWithFullMetadata(HolderLookup.Provider registries) { -@@ -406,4 +421,16 @@ public abstract class BlockEntity { - - T getOrDefault(DataComponentType type, T fallback); - } -+ // Purpur start -+ @Nullable -+ private net.minecraft.world.item.component.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 - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 12dc84361b78901dfb3725ff3add422678c19ea8..c8260dddc2852ad55bdb0091bfccb252a09a4ff3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -142,6 +142,8 @@ public class PurpurWorldConfig { - public boolean mobsIgnoreRails = false; - public boolean rainStopsAfterSleep = true; - public boolean thunderStopsAfterSleep = true; -+ public boolean persistentTileEntityLore = false; -+ public boolean persistentTileEntityDisplayName = true; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative); -@@ -166,6 +168,14 @@ 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); -+ 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); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0214-Signs-allow-color-codes.patch b/patches/server/0214-Signs-allow-color-codes.patch deleted file mode 100644 index 88c66d608f..0000000000 --- a/patches/server/0214-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 ad4ace81452d83cc01ac9de5ef323770a8a2f417..d97db4d05d7940d8408e7e0d473a3b17d956b858 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -1959,6 +1959,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - - @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 7a1d9a718dc57b5f630ca8e5358120cebaeefb9c..b51b0b0f48b1da6187387d6ec025681e10ed584d 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 -@@ -201,16 +201,31 @@ public class SignBlockEntity extends BlockEntity { - 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.util.StringUtil.filterText(filteredtext.filteredOrEmpty())).setStyle(chatmodifier)); // Paper - filter sign text to chat only -+ signtext = signtext.setMessage(i, translateColors(player, net.minecraft.util.StringUtil.filterText(filteredtext.filteredOrEmpty()), chatmodifier)); // Paper - filter sign text to chat only // Purpur - } else { -- signtext = signtext.setMessage(i, Component.literal(net.minecraft.util.StringUtil.filterText(filteredtext.raw())).setStyle(chatmodifier), Component.literal(net.minecraft.util.StringUtil.filterText(filteredtext.filteredOrEmpty())).setStyle(chatmodifier)); // Paper - filter sign text to chat only -+ signtext = signtext.setMessage(i, translateColors(player, net.minecraft.util.StringUtil.filterText(filteredtext.raw()), chatmodifier), translateColors(player, net.minecraft.util.StringUtil.filterText(filteredtext.filteredOrEmpty()), chatmodifier)); // Paper - filter sign text to chat only // Purpur - } - } - -@@ -348,6 +363,28 @@ public class SignBlockEntity extends BlockEntity { - return new CommandSourceStack(commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel) world, 2, s, (Component) object, world.getServer(), player); // Paper - Fix commands from signs not firing command events - } - -+ // Purpur start -+ public ClientboundBlockEntityDataPacket getTranslatedUpdatePacket(boolean filtered, boolean front) { -+ final CompoundTag nbt = new CompoundTag(); -+ this.saveAdditional(nbt, this.getLevel().registryAccess()); -+ 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, (blockEntity, registryAccess) -> nbt); -+ } -+ // Purpur end -+ - @Override - public ClientboundBlockEntityDataPacket getUpdatePacket() { - return ClientboundBlockEntityDataPacket.create(this); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index c8260dddc2852ad55bdb0091bfccb252a09a4ff3..1f5b541cd87c4e6fafbd1ee9b75d4a323aeacaa1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1009,6 +1009,11 @@ public class PurpurWorldConfig { - } - } - -+ 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/0215-Kelp-cave-weeping-and-twisting-vines-configurable-ma.patch b/patches/server/0215-Kelp-cave-weeping-and-twisting-vines-configurable-ma.patch deleted file mode 100644 index 0f1544c6d8..0000000000 --- a/patches/server/0215-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 81e572783157926383dd9baa58d30f5419c1616f..84d6ae4acf80f6ff4f418739a0228e740993f950 100644 ---- a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java -@@ -94,4 +94,11 @@ public class CaveVinesBlock extends GrowingPlantHeadBlock implements CaveVines { - 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 9b424d7661fedf8ee1eb9f3167c62e563f04d4d1..2af311847a085a8073e9bcb26c762d1bbe1eae2c 100644 ---- a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java -@@ -34,12 +34,12 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements - - @Override - public BlockState getStateForPlacement(RandomSource random) { -- return (BlockState) this.defaultBlockState().setValue(GrowingPlantHeadBlock.AGE, random.nextInt(25)); -+ return (BlockState) this.defaultBlockState().setValue(GrowingPlantHeadBlock.AGE, random.nextInt(getMaxGrowthAge())); // Purpur - } - - @Override - protected boolean isRandomlyTicking(BlockState state) { -- return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25; -+ return (Integer) state.getValue(GrowingPlantHeadBlock.AGE) < getMaxGrowthAge(); // Purpur - } - - @Override -@@ -55,7 +55,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); - -@@ -77,11 +77,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) { -@@ -123,13 +123,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 - } - - } -@@ -142,4 +142,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 784b19bc78c8ad9476b6dac37b6778a409a7c675..d49dd8b20d3785cc9482ed2a34fbd7aed4c9e537 100644 ---- a/src/main/java/net/minecraft/world/level/block/KelpBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/KelpBlock.java -@@ -72,4 +72,11 @@ public class KelpBlock extends GrowingPlantHeadBlock implements LiquidBlockConta - protected 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 6342bb11a162b9e6d9475c5989b1670d77e8f0fb..f8be92512446d3f0e5f0f21222bbefd04ab2838a 100644 ---- a/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/TwistingVinesBlock.java -@@ -34,4 +34,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 3dec5a082606ee35a8c8d7f746480262d6a189c5..b2f6ccae9576c176263e51a232e17a08d54543b3 100644 ---- a/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/WeepingVinesBlock.java -@@ -34,4 +34,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 f84248af574887e48180e88b3e365f7008dd1c16..80647bd8a776fea7cfedf3a991e9da73ce6c5ac1 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -315,6 +315,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); -@@ -364,6 +368,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 allowInapplicableEnchants = false; diff --git a/patches/server/0216-Mobs-always-drop-experience.patch b/patches/server/0216-Mobs-always-drop-experience.patch deleted file mode 100644 index e496fd1be3..0000000000 --- a/patches/server/0216-Mobs-always-drop-experience.patch +++ /dev/null @@ -1,2386 +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 781f0adde9194162af57523aad4b73a358f622a7..97a062c48b22c34edb2a5ad8b8c2f5443e6f7556 100644 ---- a/src/main/java/net/minecraft/world/entity/GlowSquid.java -+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java -@@ -50,6 +50,12 @@ public class GlowSquid extends Squid { - return this.level().purpurConfig.glowSquidTakeDamageFromWater; - } - // 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 - public boolean canFly() { - return this.level().purpurConfig.glowSquidsCanFly; -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 7b647f41bd5ca4fa62795ad2fbcb5b73a3405979..b9d146547a6993a960618bfd95f617046666e8eb 100644 ---- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java -+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -@@ -120,6 +120,12 @@ public class Bat extends AmbientCreature { - return this.level().purpurConfig.batTakeDamageFromWater; - } - // 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() && (float) this.tickCount % 10.0F == 0.0F; -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 e191e27308dd8c5c8702692091541756f2b57ac1..c73cc8ffe5f67826971872de78adce72d202325a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -502,6 +502,12 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - return this.level().purpurConfig.beeBreedingTicks; - } - // Purpur end - Make entity breeding times configurable -+ // Purpur start - Mobs always drop experience -+ @Override -+ protected boolean isAlwaysExperienceDropper() { -+ return this.level().purpurConfig.beeAlwaysDropExp; -+ } -+ // Purpur end - Mobs always drop experience - @Override - public boolean isSensitiveToWater() { - return this.level().purpurConfig.beeTakeDamageFromWater; -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 9bf07130a86ac8ab153eb7547c451d35eb2bb016..5a3348ca39b86cfea941fdfb98ca90a7a0ef908d 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -144,6 +144,12 @@ public class Cat extends TamableAnimal implements VariantHolder { -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 09ad3ca4fd5aaebb1f394e73bc030ffa798346ba..550b1035c2d6c6c7f0fc959e1a82c867c1f362d8 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java -@@ -94,6 +94,12 @@ public class Chicken extends Animal { - return this.level().purpurConfig.chickenTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java -index 478fb13b84609a545713371f903e4d6042747e94..a2d946aa9784e49e628fe6ebbdcbf9ce4423520f 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cod.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java -@@ -37,6 +37,12 @@ public class Cod extends AbstractSchoolingFish { - return this.level().purpurConfig.codTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java -index 42324d0d22c1f1a233aa6e6a810171b26894f8bc..845206570193682f5aca5abbb6915a64fa7f5044 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -82,6 +82,12 @@ public class Cow extends Animal { - return this.level().purpurConfig.cowTakeDamageFromWater; - } - // 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 - 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.SpawnGroupData entityData) { - this.isNaturallyAggressiveToPlayers = world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance; -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 64d1e932ac3bc673cffa324d8e75e8da596d6941..abbf13d79b4c1ed46bd486ebbd4ff5001bb096b5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java -@@ -175,6 +175,12 @@ public class Dolphin extends AgeableWaterCreature { - return this.level().purpurConfig.dolphinTakeDamageFromWater; - } - // 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 - @Nullable - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData entityData) { -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 a758f312ae4ed4a0404ca7cedbd8c7b88aef58f4..fb75b983f7c522708964a9352546ae29611bf4e9 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Fox.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java -@@ -201,6 +201,12 @@ public class Fox extends Animal implements VariantHolder { - return this.level().purpurConfig.foxTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java -index 2cde18232b4101a5b20ca7897c0b8638eab68169..caa29df16214c60c4e0a471ad6320ea5d62ba7d0 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,12 @@ public class IronGolem extends AbstractGolem implements NeutralMob { - this.summoner = summoner; - } - // Purpur end - Summoner API -+ // 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 (level().purpurConfig.ironGolemCanSwim) this.goalSelector.addGoal(0, new net.minecraft.world.entity.ai.goal.FloatGoal(this)); // Purpur - Ridables -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 a8694e39954db18840e7c066ef6bcb58e3399c0b..481d373d3906f35d0e8e7aeaef3b70b4d443894f 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java -@@ -99,6 +99,12 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { - return this.level().purpurConfig.rabbitTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -index 053c2773b52bd3399c3812452dbee2d1882d32a2..b0ac0fb823cb2860e301c63c4cd2d35cdf108275 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java -@@ -59,6 +59,12 @@ public class Salmon extends AbstractSchoolingFish implements VariantHolder, B - return this.level().purpurConfig.axolotlTakeDamageFromWater; - } - // 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 world) { - return 0.0F; -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 4a8ac3d75a3c75eea8e361d7eb729f27ea807347..214a97aa88c569f55619512e712b7bfe32d26b30 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 -@@ -122,6 +122,12 @@ public class Goat extends Animal { - return this.level().purpurConfig.goatTakeDamageFromWater; - } - // 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(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 6a511e01f8805451ce89d697568c23803b5985a8..dc1ed34349a6d0d2f233d35a81f2c28e32b10210 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 -@@ -51,6 +51,12 @@ public class Donkey extends AbstractChestedHorse { - return this.level().purpurConfig.donkeyTakeDamageFromWater; - } - // 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 - 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 c5ba0669b7e184ac42243a65a230fe325c3f84cc..b39cbc42acacdedecfc996dbe25b3773a9fae8b2 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 -@@ -78,6 +78,12 @@ public class Horse extends AbstractHorse implements VariantHolder { - return this.level().purpurConfig.horseTakeDamageFromWater; - } - // 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((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 ee8d756fed5b6458b28acbd2a62a5693baddd45d..3c265a80f55eee38406066cd02460b18fbac896d 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 -@@ -152,6 +152,12 @@ public class Llama extends AbstractChestedHorse implements VariantHolder type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) { - return checkMonsterSpawnRules(type, world, spawnReason, pos, random) && (EntitySpawnReason.isSpawner(spawnReason) || 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 d0fe85e7c44b78a078e6c2d6220849a336b079d3..83b64a8091a85b9eb940af33e4d44bd4a906b248 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java -@@ -91,6 +91,12 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob { - return this.level().purpurConfig.illusionerTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -index 8f0e9f854c4032c43cc1c9a8b087a5fa286326d8..2449a960493cbd99a22ce9b8d2fe852d1ec4b20d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java -@@ -73,6 +73,12 @@ public class MagmaCube extends Slime { - return this.level().purpurConfig.magmaCubeTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -index 71bbb3209acc12c9f20b8964770be8666a7e72f8..0682479b37064d7690c8da1569de8f7452a0439d 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -145,6 +145,12 @@ public class Phantom extends FlyingMob implements Enemy { - return crystalPosition != null; - } - // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms -+ // 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) % 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 0bc9ee0de4f30d4d57164e02992856a0bfa92041..5b98144992c366009a2b2c48e13884ceaf72acfb 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java -@@ -97,6 +97,12 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve - return this.level().purpurConfig.pillagerTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -index 7fb2af68c101dc12e60f120da1bb5c1efd20d164..95562e3614880702655df4578119e6cf3702e566 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -111,6 +111,12 @@ public class Ravager extends Raider { - return this.level().purpurConfig.ravagerTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -index 2052549f2e6b23aff5491bb0cc1af00b7f560227..78ac42c89cb768e0dfb17197a850a029937c145c 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java -@@ -126,6 +126,12 @@ public class Shulker extends AbstractGolem implements VariantHolder type, ServerLevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random - ) { -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 420118b1df271377f69e8e2bab8a62623382c819..df44d9fbb71ff252cd261fc8da6de14383e054de 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,12 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - return this.level().purpurConfig.striderBreedingTicks; - } - // 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 type, LevelAccessor world, EntitySpawnReason 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 1630074bf8065a7ecf49d426f8d5fe5e109b1d2c..ec61bc0d9b934ceed15b721e86200abfea21a923 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vex.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java -@@ -116,6 +116,12 @@ public class Vex extends Monster implements TraceableEntity { - return this.level().purpurConfig.vexTakeDamageFromWater; - } - // 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 % Vex.TICKS_PER_FLAP == 0; -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 5693081f061dcefbfda374c26a6ab5be0c2e2fcc..3d1cb875edfe6bf5c9e3f4b7dade7868b7dbfa93 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,12 @@ public class Vindicator extends AbstractIllager { - return this.level().purpurConfig.vindicatorTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java -index f77ce3de847ea5c7f40bfa6bd4bba254dc863bd0..8e7f4cb8ede8721f05c6c35e9b4d08884254853e 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Witch.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java -@@ -87,6 +87,12 @@ public class Witch extends Raider implements RangedAttackMob { - return this.level().purpurConfig.witchTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -index 1a864ebb6257f34e8fb7a543d6da19cfa96bb7d0..351a35bdcb820c9c65aeddfbe0f00486d8057f7c 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java -@@ -66,6 +66,12 @@ public class WitherSkeleton extends AbstractSkeleton { - return this.level().purpurConfig.witherSkeletonTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -index 1c2c0f94811f934804cfb0be15fa55c0281ad0ba..8aec50e7330d16bd3d0bc027c191fa1a4ce4552b 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java -@@ -115,6 +115,12 @@ public class Zoglin extends Monster implements HoglinBase { - return this.level().purpurConfig.zoglinTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -index 37ee837f6e722cd4cb07ae9fcd366a1473dc0b49..867d9ddce630482a7d3fadfe5aab213a0f912ca4 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java -@@ -154,6 +154,12 @@ public class Zombie extends Monster { - return this.level().purpurConfig.zombieTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index 40490767e474bc1e954c8f837f25268d2fd38f5b..bba3562bf316878e7b8ba6a138889d9583a1b0f6 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -135,6 +135,12 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - return this.level().purpurConfig.zombieVillagerTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -index 3070f4484276f8ea6b95134abda6409e58f1161a..c0e611f3222ffacfbd0683c8c65b778f9012a2ad 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java -@@ -109,6 +109,12 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { - return this.level().purpurConfig.zombifiedPiglinTakeDamageFromWater; - } - // 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 - 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 534626336e45da2c67cb023f4e5feea59daa8b5d..5e8da964681cb63d7733af8b5173715fd7e54a63 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 -@@ -112,6 +112,12 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - return this.level().purpurConfig.hoglinTakeDamageFromWater; - } - // 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 - @Override - public boolean canBeLeashed() { - return true; -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 f0b0642571a242f9c22febbafba587f1ca6caaaa..468be0368d0f485eaa2a5c321f45986bf5156b4b 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 -@@ -129,6 +129,12 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - return this.level().purpurConfig.piglinTakeDamageFromWater; - } - // 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 - 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 0e77e9e4a4fd14b5cecc377a03f0d6b2c9df039f..24e198440d4841daac664dc6c5a8a3dc6825b469 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 -@@ -95,6 +95,12 @@ public class PiglinBrute extends AbstractPiglin { - return this.level().purpurConfig.piglinBruteTakeDamageFromWater; - } - // 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/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index c912268081beda34c472cff13e41c9dfd51902f2..fcaf3db893d14b8359bddc1cc69da82e533ebc87 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -199,6 +199,12 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - return this.level().purpurConfig.villagerTakeDamageFromWater; - } - // 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(); // 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 2cfd9c9194edda92185adecca80c5cd140e26c9f..89ad300d0ba9088af64fb9fee19399939bb4eff6 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -113,6 +113,12 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill - return this.level().purpurConfig.wanderingTraderTakeDamageFromWater; - } - // 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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1f5b541cd87c4e6fafbd1ee9b75d4a323aeacaa1..d1a20069971e46a5bd58a31390b54f9216d9cb64 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1096,6 +1096,7 @@ public class PurpurWorldConfig { - 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); -@@ -1103,6 +1104,7 @@ public class PurpurWorldConfig { - 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; -@@ -1119,6 +1121,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); -@@ -1139,6 +1142,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; -@@ -1151,6 +1155,7 @@ public class PurpurWorldConfig { - public boolean beeTakeDamageFromWater = true; - 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); -@@ -1167,6 +1172,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; -@@ -1176,6 +1182,7 @@ public class PurpurWorldConfig { - 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); -@@ -1189,6 +1196,7 @@ public class PurpurWorldConfig { - 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; -@@ -1234,6 +1242,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); -@@ -1255,6 +1264,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; -@@ -1263,6 +1273,7 @@ public class PurpurWorldConfig { - 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); -@@ -1275,6 +1286,7 @@ public class PurpurWorldConfig { - 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; -@@ -1285,6 +1297,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); -@@ -1299,6 +1312,7 @@ 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; -@@ -1306,6 +1320,7 @@ public class PurpurWorldConfig { - 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); -@@ -1317,6 +1332,7 @@ public class PurpurWorldConfig { - 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 cowRidable = false; -@@ -1329,6 +1345,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); -@@ -1350,6 +1367,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 creakingRidable = false; -@@ -1376,6 +1394,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); -@@ -1393,6 +1412,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; -@@ -1405,6 +1425,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); -@@ -1421,6 +1442,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; -@@ -1432,6 +1454,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) { -@@ -1449,6 +1472,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; -@@ -1462,6 +1486,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); -@@ -1479,6 +1504,7 @@ 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; -@@ -1486,6 +1512,7 @@ public class PurpurWorldConfig { - 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); -@@ -1497,6 +1524,7 @@ public class PurpurWorldConfig { - 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 enderDragonRidable = false; -@@ -1542,6 +1570,7 @@ public class PurpurWorldConfig { - 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); -@@ -1565,6 +1594,7 @@ public class PurpurWorldConfig { - 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; -@@ -1573,6 +1603,7 @@ public class PurpurWorldConfig { - 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); -@@ -1585,6 +1616,7 @@ public class PurpurWorldConfig { - 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; -@@ -1594,6 +1626,7 @@ public class PurpurWorldConfig { - public double evokerScale = 1.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); -@@ -1607,6 +1640,7 @@ public class PurpurWorldConfig { - evokerScale = Mth.clamp(getDouble("mobs.evoker.attributes.scale", evokerScale), 0.0625D, 16.0D); - 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; -@@ -1618,6 +1652,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); -@@ -1633,6 +1668,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; -@@ -1655,6 +1691,7 @@ public class PurpurWorldConfig { - 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); -@@ -1668,6 +1705,7 @@ public class PurpurWorldConfig { - 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; -@@ -1682,6 +1720,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); -@@ -1704,6 +1743,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; -@@ -1712,6 +1752,7 @@ public class PurpurWorldConfig { - 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); -@@ -1719,6 +1760,7 @@ public class PurpurWorldConfig { - 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; -@@ -1728,6 +1770,7 @@ public class PurpurWorldConfig { - 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); -@@ -1736,6 +1779,7 @@ public class PurpurWorldConfig { - 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; -@@ -1743,6 +1787,7 @@ public class PurpurWorldConfig { - 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); -@@ -1754,6 +1799,7 @@ public class PurpurWorldConfig { - 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; -@@ -1770,6 +1816,7 @@ public class PurpurWorldConfig { - 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); -@@ -1783,6 +1830,7 @@ public class PurpurWorldConfig { - 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; -@@ -1794,6 +1842,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) { -@@ -1811,6 +1860,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; -@@ -1823,6 +1873,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); -@@ -1839,6 +1890,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; -@@ -1849,6 +1901,7 @@ public class PurpurWorldConfig { - 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); -@@ -1867,6 +1920,7 @@ public class PurpurWorldConfig { - 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; -@@ -1878,6 +1932,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); -@@ -1893,6 +1948,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; -@@ -1907,6 +1963,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); -@@ -1927,6 +1984,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; -@@ -1937,6 +1995,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); -@@ -1951,6 +2010,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; -@@ -1960,6 +2020,7 @@ public class PurpurWorldConfig { - 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); -@@ -1973,6 +2034,7 @@ public class PurpurWorldConfig { - 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; -@@ -1984,6 +2046,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) { -@@ -2001,6 +2064,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; -@@ -2010,6 +2074,7 @@ public class PurpurWorldConfig { - public double ocelotScale = 1.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); -@@ -2023,6 +2088,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean pandaRidable = false; -@@ -2032,6 +2098,7 @@ public class PurpurWorldConfig { - 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); -@@ -2045,6 +2112,7 @@ public class PurpurWorldConfig { - 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; -@@ -2055,6 +2123,7 @@ public class PurpurWorldConfig { - 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); -@@ -2069,6 +2138,7 @@ public class PurpurWorldConfig { - 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; -@@ -2096,6 +2166,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); -@@ -2131,6 +2202,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; -@@ -2141,6 +2213,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); -@@ -2155,6 +2228,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; -@@ -2165,6 +2239,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); -@@ -2179,6 +2254,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; -@@ -2187,6 +2263,7 @@ public class PurpurWorldConfig { - 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); -@@ -2199,6 +2276,7 @@ public class PurpurWorldConfig { - 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; -@@ -2208,6 +2286,7 @@ public class PurpurWorldConfig { - public double pillagerScale = 1.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); -@@ -2221,6 +2300,7 @@ public class PurpurWorldConfig { - pillagerScale = Mth.clamp(getDouble("mobs.pillager.attributes.scale", pillagerScale), 0.0625D, 16.0D); - 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; -@@ -2232,6 +2312,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); -@@ -2248,6 +2329,7 @@ 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; -@@ -2255,6 +2337,7 @@ public class PurpurWorldConfig { - 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); -@@ -2266,6 +2349,7 @@ public class PurpurWorldConfig { - 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; -@@ -2278,6 +2362,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); -@@ -2294,6 +2379,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; -@@ -2304,6 +2390,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); -@@ -2334,6 +2421,7 @@ public class PurpurWorldConfig { - ravagerGriefableBlocks.add(block); - } - }); -+ ravagerAlwaysDropExp = getBoolean("mobs.ravager.always-drop-exp", ravagerAlwaysDropExp); - } - - public boolean salmonRidable = false; -@@ -2341,6 +2429,7 @@ public class PurpurWorldConfig { - 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); -@@ -2352,6 +2441,7 @@ public class PurpurWorldConfig { - 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; -@@ -2362,6 +2452,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); -@@ -2376,6 +2467,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; -@@ -2390,6 +2482,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); -@@ -2408,6 +2501,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; -@@ -2419,6 +2513,7 @@ public class PurpurWorldConfig { - public double silverfishAttackDamage = 1.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); -@@ -2434,6 +2529,7 @@ public class PurpurWorldConfig { - silverfishAttackDamage = getDouble("mobs.silverfish.attributes.attack_damage", silverfishAttackDamage); - 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; -@@ -2442,6 +2538,7 @@ public class PurpurWorldConfig { - public double skeletonMaxHealth = 20.0D; - public double skeletonScale = 1.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); -@@ -2454,6 +2551,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean skeletonHorseRidable = false; -@@ -2466,6 +2564,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() { - skeletonHorseRidable = getBoolean("mobs.skeleton_horse.ridable", skeletonHorseRidable); - skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater); -@@ -2483,6 +2582,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; -@@ -2493,6 +2593,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); -@@ -2507,6 +2608,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; -@@ -2522,6 +2624,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); -@@ -2541,6 +2644,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; -@@ -2566,6 +2670,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); -@@ -2580,6 +2685,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; -@@ -2588,6 +2694,7 @@ public class PurpurWorldConfig { - 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); -@@ -2600,6 +2707,7 @@ public class PurpurWorldConfig { - 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; -@@ -2608,6 +2716,7 @@ public class PurpurWorldConfig { - 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); -@@ -2620,6 +2729,7 @@ public class PurpurWorldConfig { - 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; -@@ -2630,6 +2740,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); -@@ -2644,6 +2755,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; -@@ -2666,6 +2778,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); -@@ -2685,6 +2798,7 @@ 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; -@@ -2692,6 +2806,7 @@ public class PurpurWorldConfig { - 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); -@@ -2703,6 +2818,7 @@ public class PurpurWorldConfig { - 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; -@@ -2712,6 +2828,7 @@ public class PurpurWorldConfig { - 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); -@@ -2725,6 +2842,7 @@ public class PurpurWorldConfig { - 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; -@@ -2734,6 +2852,7 @@ public class PurpurWorldConfig { - 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); -@@ -2747,6 +2866,7 @@ public class PurpurWorldConfig { - 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; -@@ -2764,6 +2884,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); -@@ -2785,6 +2906,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; -@@ -2794,6 +2916,7 @@ public class PurpurWorldConfig { - 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); -@@ -2807,6 +2930,7 @@ public class PurpurWorldConfig { - 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; -@@ -2819,6 +2943,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); -@@ -2835,6 +2960,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; -@@ -2852,6 +2978,7 @@ public class PurpurWorldConfig { - 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); -@@ -2864,6 +2991,7 @@ public class PurpurWorldConfig { - 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; -@@ -2879,6 +3007,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); -@@ -2902,6 +3031,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; -@@ -2910,6 +3040,7 @@ public class PurpurWorldConfig { - 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); -@@ -2922,6 +3053,7 @@ public class PurpurWorldConfig { - 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; -@@ -2934,6 +3066,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); -@@ -2954,6 +3087,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; -@@ -2962,6 +3096,7 @@ public class PurpurWorldConfig { - 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); -@@ -2974,6 +3109,7 @@ public class PurpurWorldConfig { - 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; -@@ -2988,6 +3124,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); -@@ -3006,6 +3143,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 zombieHorseRidable = false; -@@ -3019,6 +3157,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() { - zombieHorseRidable = getBoolean("mobs.zombie_horse.ridable", zombieHorseRidable); - zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater); -@@ -3037,6 +3176,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; -@@ -3052,6 +3192,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); -@@ -3071,6 +3212,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; -@@ -3084,6 +3226,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); -@@ -3101,6 +3244,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/0217-Grindstone-API.patch b/patches/server/0217-Grindstone-API.patch deleted file mode 100644 index eca41ec5ae..0000000000 --- a/patches/server/0217-Grindstone-API.patch +++ /dev/null @@ -1,36 +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 cc229f3e1e9527cbedf929e326731943bb513dae..111da7435f0abb5a57bd2c5fecead2380ac4347a 100644 ---- a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java -@@ -96,12 +96,14 @@ 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) -> { - if (world 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(world, blockposition), this.getExperienceAmount(world)); - event.callEvent(); -- ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), event.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); -+ 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 -+ ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), grindstoneTakeResultEvent.getExperienceAmount(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); // Purpur - // Paper end - Fire BlockExpEvent on grindstone use - } - -@@ -386,7 +388,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/0218-Ability-for-hoe-to-replant-crops-and-nether-warts.patch b/patches/server/0218-Ability-for-hoe-to-replant-crops-and-nether-warts.patch deleted file mode 100644 index d4e0e65852..0000000000 --- a/patches/server/0218-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 eb324fda54ada3ed7941713a784ed2d686ec8c4b..09cc76f3fee4a767c9ec3fa592f2c3c6146344ec 100644 ---- a/src/main/java/net/minecraft/world/level/block/BushBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BushBlock.java -@@ -55,4 +55,24 @@ public abstract class BushBlock extends Block { - protected boolean isPathfindable(BlockState state, PathComputationType type) { - return type == PathComputationType.AIR && !this.hasCollision ? true : super.isPathfindable(state, 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 00a06146e119a47eeaf66d240b8dd84e38498676..34f338a246824dbabc7bc386b74cb62c78a8f1b6 100644 ---- a/src/main/java/net/minecraft/world/level/block/CropBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java -@@ -216,4 +216,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, 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 - } -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 7bb4994d6474c8ea59c102009253552020691b8f..b571bca4375ca7caf9b75dbf84009cb0604b66ad 100644 ---- a/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java -@@ -68,4 +68,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, 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 - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index d1a20069971e46a5bd58a31390b54f9216d9cb64..6b579962256be91923531fa4dac21a34b581386b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -509,6 +509,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(); -@@ -783,6 +785,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/0219-Turtle-eggs-random-tick-crack-chance.patch b/patches/server/0219-Turtle-eggs-random-tick-crack-chance.patch deleted file mode 100644 index e26d4a1142..0000000000 --- a/patches/server/0219-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 30af2a98a52208c3a36dfaad474582806f86aede..6f02cec4384382569ad7d7c830dd6349aeac5293 100644 ---- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -@@ -171,7 +171,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 6b579962256be91923531fa4dac21a34b581386b..0d5fb6e2b4d46c6de5222190d2c5535a233c1067 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1043,11 +1043,13 @@ public class PurpurWorldConfig { - public boolean turtleEggsBreakFromItems = false; - public boolean turtleEggsBreakFromMinecarts = false; - 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/0220-Mob-head-visibility-percent.patch b/patches/server/0220-Mob-head-visibility-percent.patch deleted file mode 100644 index 525033eaa3..0000000000 --- a/patches/server/0220-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 c7e2330e7f30081dbf5d79f08c8adb1d7d84fa03..480617d9e59bfbb21cccf2555616ad3985b7f7be 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1069,9 +1069,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 0d5fb6e2b4d46c6de5222190d2c5535a233c1067..2c1a380a423e4858f7c1c617fb53e169c882247f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1401,6 +1401,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); -@@ -1419,6 +1420,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; -@@ -2246,6 +2248,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); -@@ -2261,6 +2264,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; -@@ -2545,6 +2549,7 @@ public class PurpurWorldConfig { - public double skeletonScale = 1.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); -@@ -2558,6 +2563,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean skeletonHorseRidable = false; -@@ -3131,6 +3137,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); -@@ -3150,6 +3157,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 zombieHorseRidable = false; diff --git a/patches/server/0221-Configurable-valid-characters-for-usernames.patch b/patches/server/0221-Configurable-valid-characters-for-usernames.patch deleted file mode 100644 index 214ad260ce..0000000000 --- a/patches/server/0221-Configurable-valid-characters-for-usernames.patch +++ /dev/null @@ -1,34 +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/util/StringUtil.java b/src/main/java/net/minecraft/util/StringUtil.java -index 6c33002dc8bbb3759c3156302ab7d1f26ce5e8ee..c89fc375aff548a2b03eaf4da3b6a075012df012 100644 ---- a/src/main/java/net/minecraft/util/StringUtil.java -+++ b/src/main/java/net/minecraft/util/StringUtil.java -@@ -69,6 +69,7 @@ public class StringUtil { - - // Paper start - Username validation - public static boolean isReasonablePlayerName(final String name) { -+ if (true) return org.purpurmc.purpur.PurpurConfig.usernameValidCharactersPattern.matcher(name).matches(); // Purpur - if (name.isEmpty() || name.length() > 16) { - return false; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 80647bd8a776fea7cfedf3a991e9da73ce6c5ac1..00f02ad5f248120ce031048e02fe1bacda5567b7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -455,4 +455,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/0222-Stop-bees-from-dying-after-stinging.patch b/patches/server/0222-Stop-bees-from-dying-after-stinging.patch deleted file mode 100644 index 89b001233d..0000000000 --- a/patches/server/0222-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 c73cc8ffe5f67826971872de78adce72d202325a..19d1facb4173c11bb3a1d519603e4ec6906cdaa3 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -465,6 +465,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - this.hurtServer(world, 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 2c1a380a423e4858f7c1c617fb53e169c882247f..d71abce6d9dc812d46eda7acff5249621e5b0ae7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1162,6 +1162,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); -@@ -1179,6 +1180,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/0223-Give-bee-counts-in-beehives-to-Purpur-clients.patch b/patches/server/0223-Give-bee-counts-in-beehives-to-Purpur-clients.patch deleted file mode 100644 index 90bebd470b..0000000000 --- a/patches/server/0223-Give-bee-counts-in-beehives-to-Purpur-clients.patch +++ /dev/null @@ -1,184 +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 80a8bd2dc32763f8ee2062c2d1b36188f2532523..1dc4476ca1fc41030001d4d23ffff1b810a056cd 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1170,6 +1170,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop STREAM_CODEC = CustomPacketPayload.codec(ClientboundBeehivePayload::write, ClientboundBeehivePayload::new); -+ public static final Type TYPE = new Type<>(ResourceLocation.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/src/main/java/org/purpurmc/purpur/network/ServerboundBeehivePayload.java b/src/main/java/org/purpurmc/purpur/network/ServerboundBeehivePayload.java -new file mode 100644 -index 0000000000000000000000000000000000000000..fa72769e06061609e1e658a0250e99c8cb026c0e ---- /dev/null -+++ b/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.ResourceLocation; -+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<>(ResourceLocation.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/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java b/src/main/java/org/purpurmc/purpur/task/BeehiveTask.java -new file mode 100644 -index 0000000000000000000000000000000000000000..664f9d5e1ce5e2787bf699bd11758b9e3aa8ed3a ---- /dev/null -+++ b/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/patches/server/0224-Configurable-farmland-trample-height.patch b/patches/server/0224-Configurable-farmland-trample-height.patch deleted file mode 100644 index dec7142660..0000000000 --- a/patches/server/0224-Configurable-farmland-trample-height.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -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 fc5a755c558714442c1e12e88ee05764d6c1d9f4..f1ef2eda3282b3bcd99e388dc56d5542cd93bedb 100644 ---- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java -@@ -112,7 +112,7 @@ public class FarmBlock extends Block { - 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 instanceof ServerLevel worldserver) { -- if (world.random.nextFloat() < fallDistance - 0.5F && entity instanceof LivingEntity && (entity instanceof Player || worldserver.purpurConfig.farmlandBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { // Purpur - Add mobGriefing bypass to everything affected -+ if ((worldserver.purpurConfig.farmlandTrampleHeight >= 0D ? fallDistance >= worldserver.purpurConfig.farmlandTrampleHeight : world.random.nextFloat() < fallDistance - 0.5F) && entity instanceof LivingEntity && (entity instanceof Player || worldserver.purpurConfig.farmlandBypassMobGriefing ^ worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { // Purpur - Add mobGriefing bypass to everything affected // Purpur - Configurable farmland trample height - // 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 d71abce6d9dc812d46eda7acff5249621e5b0ae7..c5e894560b3ac371da123a339355fa89dd442ee4 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -936,6 +936,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); -@@ -943,6 +944,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/0225-Configurable-player-pickup-exp-delay.patch b/patches/server/0225-Configurable-player-pickup-exp-delay.patch deleted file mode 100644 index 2eb1958fbf..0000000000 --- a/patches/server/0225-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 f1eb74a43881400f23f3750c909f999b35c2f5ce..ba4fe614e2a3378f17b544c78c92a271523e8d37 100644 ---- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -+++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java -@@ -335,7 +335,7 @@ public class ExperienceOrb extends Entity { - public void playerTouch(Player player) { - if (player instanceof ServerPlayer entityplayer) { - if (player.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(entityplayer.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper - PlayerPickupExperienceEvent -- 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(entityplayer, 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 dda9391962b7360714cc1491e6cabfc2616dab25..7893d257deef3c1bb0187c6a0b04659716b520f9 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -665,7 +665,7 @@ public abstract class Player extends LivingEntity { - while (iterator.hasNext()) { - Entity entity = (Entity) iterator.next(); - -- 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 c5e894560b3ac371da123a339355fa89dd442ee4..724dd962259b10649e12bf26b81fc87d315def8c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -423,6 +423,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); -@@ -448,6 +449,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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0226-Allow-void-trading.patch b/patches/server/0226-Allow-void-trading.patch deleted file mode 100644 index b903321abe..0000000000 --- a/patches/server/0226-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 0487d55861302e1bf84225901d873b02c2f11d6e..1b647544ebd578d1f8fbb6b65a2c27ac07761683 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -2840,7 +2840,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - // 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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 724dd962259b10649e12bf26b81fc87d315def8c..0292d3dac3a6b348784d3b75e210e023a1d146d2 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 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); -@@ -450,6 +451,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); - } - - public boolean silkTouchEnabled = false; diff --git a/patches/server/0227-Configurable-phantom-size.patch b/patches/server/0227-Configurable-phantom-size.patch deleted file mode 100644 index cae629204a..0000000000 --- a/patches/server/0227-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 0682479b37064d7690c8da1569de8f7452a0439d..860162797972263283737e8f30d8b784955206be 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java -@@ -274,7 +274,11 @@ public class Phantom extends FlyingMob implements Enemy { - @Override - public SpawnGroupData finalizeSpawn(ServerLevelAccessor world, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData entityData) { - 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); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0292d3dac3a6b348784d3b75e210e023a1d146d2..fc11b643c938cf7d7bd088825dfd89afef15401d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2183,6 +2183,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); -@@ -2219,6 +2221,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/0228-Max-joins-per-second.patch b/patches/server/0228-Max-joins-per-second.patch deleted file mode 100644 index 2343aeee5b..0000000000 --- a/patches/server/0228-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 3c866432c8a938c677a315612f3e159bda67a2a2..8661c1b1cfe2b3db000e1f08814fd4409c4b7fab 100644 ---- a/src/main/java/net/minecraft/network/Connection.java -+++ b/src/main/java/net/minecraft/network/Connection.java -@@ -617,11 +617,20 @@ public class Connection extends SimpleChannelInboundHandler> { - 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 - 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 -+ if (org.purpurmc.purpur.PurpurConfig.maxJoinsPerSecond) { -+ if (++Connection.tickSecond > 20) { -+ Connection.tickSecond = 0; -+ Connection.joinAttemptsThisTick = 0; -+ } -+ } else -+ // Purpur end - Connection.joinAttemptsThisTick = 0; - } - // Paper end - Buffer joins to world -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 60bb703fdf582bc3ec1081f31818d696e3b276ed..222d1850a9c0c6907dc9de534cff64c122a0bb89 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -457,8 +457,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/0229-Configurable-minimum-demand-for-trades.patch b/patches/server/0229-Configurable-minimum-demand-for-trades.patch deleted file mode 100644 index 594b0d5731..0000000000 --- a/patches/server/0229-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 fcaf3db893d14b8359bddc1cc69da82e533ebc87..e03066a9e336fb67f729a84404ef8b37208fa77d 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -548,7 +548,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 0efc8d997b34302c3e0a5d7ec73a11a940dbeefe..af157881d440b34cfe79fbc9b03cc9ef28515eb8 100644 ---- a/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java -+++ b/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java -@@ -131,7 +131,12 @@ public class MerchantOffer { - } - - public void updateDemand() { -- this.demand = Math.max(0, this.demand + this.uses - (this.maxUses - this.uses)); // Paper - Fix MC-163962 -+ // Purpur start -+ this.updateDemand(0); -+ } -+ public void updateDemand(int minimumDemand) { -+ this.demand = Math.max(minimumDemand, this.demand + this.uses - (this.maxUses - this.uses)); // Paper - Fix MC-163962 -+ // 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 fc11b643c938cf7d7bd088825dfd89afef15401d..fa09bd0e91d3c71d960e316f792323b763569b6e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2914,6 +2914,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); -@@ -2936,6 +2937,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/0230-Lobotomize-stuck-villagers.patch b/patches/server/0230-Lobotomize-stuck-villagers.patch deleted file mode 100644 index 4ab49d33c7..0000000000 --- a/patches/server/0230-Lobotomize-stuck-villagers.patch +++ /dev/null @@ -1,139 +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 e03066a9e336fb67f729a84404ef8b37208fa77d..4170bd886f68e3c21df6f9680ad6f6aa26d74c01 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 - }, MemoryModuleType.MEETING_POINT, (entityvillager, holder) -> { - return holder.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 entityType, Level world) { - this(entityType, world, VillagerType.PLAINS); -@@ -205,6 +207,49 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - return this.level().purpurConfig.villagerAlwaysDropExp; - } - // Purpur end - Mobs always drop experience -+ // 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(); // CraftBukkit - decompile error -@@ -299,11 +344,19 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - // Paper start - EAR 2 - this.customServerAiStep(world, false); - } -- protected void customServerAiStep(ServerLevel world, final boolean inactive) { -+ protected void customServerAiStep(ServerLevel world, boolean inactive) { // Purpur - not final - // Paper end - EAR 2 - ProfilerFiller gameprofilerfiller = Profiler.get(); - - gameprofilerfiller.push("villagerBrain"); -+ // Purpur start -+ if (this.level().purpurConfig.villagerLobotomizeEnabled) { -+ // treat as inactive if lobotomized -+ inactive = inactive || checkLobotomized(); -+ } else { -+ this.isLobotomized = false; -+ } -+ // Purpur end - // Pufferfish start - if (!inactive && (getRider() == null || !this.isControllable()) /*&& this.behaviorTick++ % this.activatedPriority == 0*/) { - this.getBrain().tick(world, this); // Paper // Purpur - Ridables -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java -index 8e895d6f84f7d84b219f2424909dd42e5f08dec4..e5597563a6ed620ab9c9e81be4bad56fd5308305 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java -@@ -375,4 +375,11 @@ public class CraftVillager extends CraftAbstractVillager implements Villager { - getHandle().getGossips().gossips.clear(); - } - // Paper end -+ -+ // Purpur start - Lobotomize stuck villagers -+ @Override -+ public boolean isLobotomized() { -+ return getHandle().isLobotomized(); -+ } -+ // Purpur end - Lobotomize stuck villagers - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index fa09bd0e91d3c71d960e316f792323b763569b6e..30d54adaae14884832387951d47872bedaf087a0 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2915,6 +2915,9 @@ public class PurpurWorldConfig { - public boolean villagerAllowTrading = true; - public boolean villagerAlwaysDropExp = false; - public int villagerMinimumDemand = 0; -+ public boolean villagerLobotomizeEnabled = false; -+ public int villagerLobotomizeCheckInterval = 100; -+ public boolean villagerLobotomizeWaitUntilTradeLocked = false; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -2938,6 +2941,18 @@ 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); -+ villagerLobotomizeWaitUntilTradeLocked = getBoolean("mobs.villager.lobotomize.wait-until-trade-locked", villagerLobotomizeWaitUntilTradeLocked); - } - - public boolean vindicatorRidable = false; diff --git a/patches/server/0231-Option-for-villager-display-trade-item.patch b/patches/server/0231-Option-for-villager-display-trade-item.patch deleted file mode 100644 index d771761369..0000000000 --- a/patches/server/0231-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 18dad0825616c4167a0a7555689ee64910a87e09..6945992491027d43eca4f1ca697ad45ce06ded55 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 -@@ -46,6 +46,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 30d54adaae14884832387951d47872bedaf087a0..b5b0a603c8c6c3e71e1540012227500276022263 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2918,6 +2918,7 @@ public class PurpurWorldConfig { - public boolean villagerLobotomizeEnabled = false; - public int villagerLobotomizeCheckInterval = 100; - public boolean villagerLobotomizeWaitUntilTradeLocked = false; -+ public boolean villagerDisplayTradeItem = true; - private void villagerSettings() { - villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); - villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); -@@ -2953,6 +2954,7 @@ public class PurpurWorldConfig { - 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); - } - - public boolean vindicatorRidable = false; diff --git a/patches/server/0232-MC-238526-Fix-spawner-not-spawning-water-animals-cor.patch b/patches/server/0232-MC-238526-Fix-spawner-not-spawning-water-animals-cor.patch deleted file mode 100644 index 63465cb8b4..0000000000 --- a/patches/server/0232-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 8c4532a250f8679d729a35c17e9b5bd339264450..2b8336bd88641cfb29e94c8f01abfbdb39938bf3 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java -+++ b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java -@@ -74,6 +74,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 - Make water animal spawn height configurable -- return pos.getY() >= j && pos.getY() <= i && world.getFluidState(pos.below()).is(FluidTags.WATER) && world.getBlockState(pos.above()).is(Blocks.WATER); -+ return ((reason == EntitySpawnReason.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 b5b0a603c8c6c3e71e1540012227500276022263..6572bbaf5d5548b1d2d6c224a9dcbbfe31f34339 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1030,8 +1030,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/0233-Config-for-mob-last-hurt-by-player-time.patch b/patches/server/0233-Config-for-mob-last-hurt-by-player-time.patch deleted file mode 100644 index 28635779ea..0000000000 --- a/patches/server/0233-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 480617d9e59bfbb21cccf2555616ad3985b7f7be..b7607d917899823b0b95167a9dced495b7ae110f 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1695,13 +1695,13 @@ public abstract class LivingEntity extends Entity implements Attackable { - Entity entity = damageSource.getEntity(); - - if (entity instanceof net.minecraft.world.entity.player.Player entityhuman) { -- this.lastHurtByPlayerTime = 100; -+ this.lastHurtByPlayerTime = this.level().purpurConfig.mobLastHurtByPlayerTime; // Purpur - this.lastHurtByPlayer = entityhuman; - return entityhuman; - } else { - if (entity instanceof Wolf entitywolf) { - if (entitywolf.isTame()) { -- this.lastHurtByPlayerTime = 100; -+ this.lastHurtByPlayerTime = this.level().purpurConfig.mobLastHurtByPlayerTime; // Purpur - LivingEntity entityliving = entitywolf.getOwner(); - - if (entityliving 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 7ccc40555964b906be6987532de1f319e38741ce..72498e233ece886941cca268e729336d66042402 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -523,7 +523,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 6572bbaf5d5548b1d2d6c224a9dcbbfe31f34339..13791cc140fa7643991f5c958f48bf4693b20895 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -144,6 +144,7 @@ public class PurpurWorldConfig { - public boolean thunderStopsAfterSleep = true; - public boolean persistentTileEntityLore = false; - public boolean persistentTileEntityDisplayName = 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); -@@ -176,6 +177,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0235-Option-to-disable-turtle-egg-trampling-with-feather-.patch b/patches/server/0235-Option-to-disable-turtle-egg-trampling-with-feather-.patch deleted file mode 100644 index 11fd438c1c..0000000000 --- a/patches/server/0235-Option-to-disable-turtle-egg-trampling-with-feather-.patch +++ /dev/null @@ -1,42 +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 6f02cec4384382569ad7d7c830dd6349aeac5293..cf86448e2067712863d30c9aecc48daedefd227f 100644 ---- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java -@@ -220,6 +220,12 @@ public class TurtleEggBlock extends Block { - if (!(entity instanceof LivingEntity)) { - return false; - } -+ // Purpur start - Option to disable turtle egg trampling with feather falling -+ if (world.purpurConfig.turtleEggsTramplingFeatherFalling) { -+ java.util.Iterator armor = ((LivingEntity) entity).getArmorSlots().iterator(); -+ return !armor.hasNext() || net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FEATHER_FALLING, armor.next()) < (int) entity.fallDistance; -+ } -+ // Purpur end - Option to disable turtle egg trampling with feather falling - if (entity instanceof Player) return true; - - return world.purpurConfig.turtleEggsBypassMobGriefing ^ world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur - Add mobGriefing bypass to everything affected -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 1290e73e4e741e7530481885994f16c2fd5db32c..7cdcea1f5170bc095b365f126a05cb4a6ca70087 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1058,12 +1058,14 @@ public class PurpurWorldConfig { - public boolean turtleEggsBreakFromMinecarts = false; - 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/0236-Add-toggle-for-enchant-level-clamping.patch b/patches/server/0236-Add-toggle-for-enchant-level-clamping.patch deleted file mode 100644 index 99b336bea4..0000000000 --- a/patches/server/0236-Add-toggle-for-enchant-level-clamping.patch +++ /dev/null @@ -1,80 +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/server/Main.java b/src/main/java/net/minecraft/server/Main.java -index 300a044bb0f0e377133f24469cea1a9669de6e58..978c0d7296f400fe2ebda89e4f61386e6e87fe0c 100644 ---- a/src/main/java/net/minecraft/server/Main.java -+++ b/src/main/java/net/minecraft/server/Main.java -@@ -122,6 +122,11 @@ public class Main { - JvmProfiler.INSTANCE.start(Environment.SERVER); - } - -+ // Purpur start - 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); -+ // Purpur end - load config files early -+ - io.papermc.paper.plugin.PluginInitializerManager.load(optionset); // Paper - Bootstrap.bootStrap(); - Bootstrap.validate(); -diff --git a/src/main/java/net/minecraft/world/item/enchantment/ItemEnchantments.java b/src/main/java/net/minecraft/world/item/enchantment/ItemEnchantments.java -index cfc6a657cae92c68868a76c1b7b1febe2a16e9f4..a12c08da793139e39dc11c213c94796b83bd8240 100644 ---- a/src/main/java/net/minecraft/world/item/enchantment/ItemEnchantments.java -+++ b/src/main/java/net/minecraft/world/item/enchantment/ItemEnchantments.java -@@ -35,7 +35,7 @@ public class ItemEnchantments implements TooltipProvider { - private static final java.util.Comparator> ENCHANTMENT_ORDER = java.util.Comparator.comparing(Holder::getRegisteredName); - public static final ItemEnchantments EMPTY = new ItemEnchantments(new Object2IntAVLTreeMap<>(ENCHANTMENT_ORDER), true); - // Paper end -- 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 - private static final Codec>> LEVELS_CODEC = Codec.unboundedMap( - Enchantment.CODEC, LEVEL_CODEC - )// Paper start - sort enchantments -@@ -69,7 +69,7 @@ public class ItemEnchantments implements TooltipProvider { - - for (Entry> entry : enchantments.object2IntEntrySet()) { - int i = entry.getIntValue(); -- if (i < 0 || i > 255) { -+ if (i < 0 || i > (org.purpurmc.purpur.PurpurConfig.clampEnchantLevels ? 255 : 32767)) { // Purpur - throw new IllegalArgumentException("Enchantment " + entry.getKey() + " has invalid level " + i); - } - } -@@ -164,13 +164,13 @@ public class ItemEnchantments implements TooltipProvider { - 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 - } - } - - 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 - } - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 222d1850a9c0c6907dc9de534cff64c122a0bb89..6cd88ca590658adb8f79296fc74af3536e2e1be3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -399,6 +399,7 @@ public class PurpurConfig { - 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); -@@ -422,6 +423,7 @@ public class PurpurConfig { - 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; diff --git a/patches/server/0238-Implement-configurable-search-radius-for-villagers-t.patch b/patches/server/0238-Implement-configurable-search-radius-for-villagers-t.patch deleted file mode 100644 index 32df779fdf..0000000000 --- a/patches/server/0238-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 4170bd886f68e3c21df6f9680ad6f6aa26d74c01..e7ac8d4c6e839c19776b9c99bf6658104607a579 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -1075,6 +1075,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 7cdcea1f5170bc095b365f126a05cb4a6ca70087..f789100866e21cdf7b2891dfcb6913081f597d79 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2929,6 +2929,8 @@ public class PurpurWorldConfig { - public int villagerLobotomizeCheckInterval = 100; - public boolean villagerLobotomizeWaitUntilTradeLocked = false; - 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); -@@ -2965,6 +2967,8 @@ public class PurpurWorldConfig { - 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); - } - - public boolean vindicatorRidable = false; diff --git a/patches/server/0239-Stonecutter-damage.patch b/patches/server/0239-Stonecutter-damage.patch deleted file mode 100644 index daf4a50e94..0000000000 --- a/patches/server/0239-Stonecutter-damage.patch +++ /dev/null @@ -1,165 +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 254aea4138afa1009a3c949a24c1a0fb8edbbd1d..10afc08930d7fe27ffa396ec5a10afb7769a3f0b 100644 ---- a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -+++ b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -@@ -101,6 +101,10 @@ public class CombatTracker { - // 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/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -index 43985b4442145728c28bbeb9d18444a7c8f3a41f..ccdf9d40b767115e0e6db2c29af42f5ec4c40f85 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -@@ -30,6 +30,7 @@ public class DamageSource { - private boolean melting = false; - private boolean poison = false; - private boolean scissors = false; // Purpur - Dont run with scissors! -+ private boolean stonecutter = false; // Purpur - Stonecutter damage - @Nullable - private Entity customEventDamager = null; // This field is a helper for when causing entity damage is not set by vanilla // Paper - fix DamageSource API - -@@ -70,6 +71,16 @@ public class DamageSource { - return this.scissors; - } - // Purpur end - Dont run with scissors! -+ // Purpur start - - Stonecutter damage -+ public DamageSource stonecutter() { -+ this.stonecutter = true; -+ return this; -+ } -+ -+ public boolean isStonecutter() { -+ return this.stonecutter; -+ } -+ // Purpur end - Stonecutter damage - - // Paper start - fix DamageSource API - @Nullable -@@ -130,6 +141,7 @@ public class DamageSource { - damageSource.poison = this.isPoison(); - damageSource.melting = this.isMelting(); - damageSource.scissors = this.isScissors(); // Purpur - Dont run with scissors! -+ damageSource.stonecutter = this.isStonecutter(); // Purpur - Stonecutter damage - return damageSource; - } - // CraftBukkit end -diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSources.java b/src/main/java/net/minecraft/world/damagesource/DamageSources.java -index a4f710ad1aecd8265cde8d71d55eea952cd3c03b..820533b6a13b1f8abbfe378de6b5d66ce2b79835 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSources.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSources.java -@@ -47,12 +47,14 @@ public class DamageSources { - private final DamageSource melting; - private final DamageSource poison; - private final DamageSource scissors; // Purpur - Dont run with scissors! -+ private final DamageSource stonecutter; // Purpur - Stonecutter damage - - public DamageSources(RegistryAccess registryManager) { - this.damageTypes = registryManager.lookupOrThrow(Registries.DAMAGE_TYPE); - this.melting = this.source(DamageTypes.ON_FIRE).melting(); - this.poison = this.source(DamageTypes.MAGIC).poison(); - this.scissors = this.source(DamageTypes.MAGIC).scissors(); // Purpur - Dont run with scissors! -+ this.stonecutter = this.source(DamageTypes.MAGIC).stonecutter(); // Purpur - Stonecutter damage - // CraftBukkit end - this.inFire = this.source(DamageTypes.IN_FIRE); - this.campfire = this.source(DamageTypes.CAMPFIRE); -@@ -109,6 +111,11 @@ public class DamageSources { - } - // 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/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java b/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java -index f1366aea49206afcd64bf058ee673d6a562315c5..e93d1eba4fcd02e15287a1a66da94e695806a470 100644 ---- a/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java -@@ -93,4 +93,14 @@ public class StonecutterBlock extends Block { - protected boolean isPathfindable(BlockState state, PathComputationType type) { - 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().directBlock(level, pos), level.purpurConfig.stonecutterDamage); -+ } -+ super.stepOn(level, pos, state, entity); -+ } -+ // Purpur end - Stonecutter damage - } -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 e295fe4aac742ff8942b23456fdce8d7ff944e90..b65512f65e06865cc4d2964bd4ca2806784be738 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java -@@ -491,7 +491,7 @@ public class WalkNodeEvaluator extends NodeEvaluator { - 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/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -index eb2f9bfdaf3ed8a684337a15365e70174d1533b3..d52d41d8c56e017f95914da19b05c3d79f8f1640 100644 ---- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -@@ -1140,7 +1140,7 @@ public class CraftEventFactory { - return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.LAVA, bukkitDamageSource, modifiers, modifierFunctions, cancelled); - } else if (source.getDirectBlock() != null) { - DamageCause cause; -- if (source.is(DamageTypes.CACTUS) || source.is(DamageTypes.SWEET_BERRY_BUSH) || source.is(DamageTypes.STALAGMITE) || source.is(DamageTypes.FALLING_STALACTITE) || source.is(DamageTypes.FALLING_ANVIL)) { -+ if (source.is(DamageTypes.CACTUS) || source.is(DamageTypes.SWEET_BERRY_BUSH) || source.is(DamageTypes.STALAGMITE) || source.is(DamageTypes.FALLING_STALACTITE) || source.is(DamageTypes.FALLING_ANVIL) || source.isStonecutter()) { // Purpur - Stonecutter damage - cause = DamageCause.CONTACT; - } else if (source.is(DamageTypes.HOT_FLOOR)) { - cause = DamageCause.HOT_FLOOR; -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 6cd88ca590658adb8f79296fc74af3536e2e1be3..4ad201f8457dca2305224cc74a1f55b90dd60140 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -212,8 +212,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 f789100866e21cdf7b2891dfcb6913081f597d79..2a53cf6fcab47e49735a31ed9eb1fb668caa2e4c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1053,6 +1053,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 = false; - public boolean turtleEggsBreakFromItems = false; - public boolean turtleEggsBreakFromMinecarts = false; diff --git a/patches/server/0240-Configurable-damage-settings-for-magma-blocks.patch b/patches/server/0240-Configurable-damage-settings-for-magma-blocks.patch deleted file mode 100644 index 14b80320d8..0000000000 --- a/patches/server/0240-Configurable-damage-settings-for-magma-blocks.patch +++ /dev/null @@ -1,35 +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 7ffdcf18bf4bd8b5325c76945b2d80ca3fe52958..4adbbd27c5b2e4cef630c6c8aae38d3f2b94c11e 100644 ---- a/src/main/java/net/minecraft/world/level/block/MagmaBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/MagmaBlock.java -@@ -29,7 +29,7 @@ public class MagmaBlock extends Block { - - @Override - public void stepOn(Level world, BlockPos pos, BlockState state, Entity entity) { -- if (!entity.isSteppingCarefully() && entity instanceof LivingEntity) { -+ if ((!entity.isSteppingCarefully() || world.purpurConfig.magmaBlockDamageWhenSneaking) && entity instanceof LivingEntity) { // Purpur - Configurable damage settings for magma blocks - entity.hurt(world.damageSources().hotFloor().directBlock(world, pos), 1.0F); // CraftBukkit - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 2a53cf6fcab47e49735a31ed9eb1fb668caa2e4c..18ea922e71dc9de3e9eccac2d30c5f2776928a0b 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -994,6 +994,11 @@ public class PurpurWorldConfig { - 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 powderSnowBypassMobGriefing = false; - private void powderSnowSettings() { - powderSnowBypassMobGriefing = getBoolean("blocks.powder_snow.bypass-mob-griefing", powderSnowBypassMobGriefing); diff --git a/patches/server/0241-Add-config-for-snow-on-blue-ice.patch b/patches/server/0241-Add-config-for-snow-on-blue-ice.patch deleted file mode 100644 index 4c1f98d299..0000000000 --- a/patches/server/0241-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 9908a0b5b1fec5f9de518a733f7abbbff7e1a9f9..0ad444cf7f798f63e9140a42c5d5d8cab0fcf14f 100644 ---- a/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SnowLayerBlock.java -@@ -88,6 +88,12 @@ public class SnowLayerBlock extends Block { - protected 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 18ea922e71dc9de3e9eccac2d30c5f2776928a0b..cbf26d5f0a3d615883b17c65709d078c72c46bbf 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -975,9 +975,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/0242-Skeletons-eat-wither-roses.patch b/patches/server/0242-Skeletons-eat-wither-roses.patch deleted file mode 100644 index 982e12f527..0000000000 --- a/patches/server/0242-Skeletons-eat-wither-roses.patch +++ /dev/null @@ -1,112 +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 93b570c0d18f2fc76180c5c6ea292e191cb81be2..f1a9546aaa8499b198228f6a684850f7b20e67c9 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java -@@ -17,6 +17,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; -@@ -182,4 +192,64 @@ 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(), net.minecraft.world.entity.EntitySpawnReason.CONVERSION); -+ 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; -+ } -+ -+ 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) { -+ ((ServerLevel) level()).sendParticlesSource(((ServerLevel) level()).players(), null, 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 - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index cbf26d5f0a3d615883b17c65709d078c72c46bbf..c122af4829a2edd3977af0310b27bb984d8eaad5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2589,6 +2589,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); -@@ -2603,6 +2604,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 skeletonHorseRidable = false; diff --git a/patches/server/0243-Enchantment-Table-Persists-Lapis.patch b/patches/server/0243-Enchantment-Table-Persists-Lapis.patch deleted file mode 100644 index 1b24b3a211..0000000000 --- a/patches/server/0243-Enchantment-Table-Persists-Lapis.patch +++ /dev/null @@ -1,153 +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 bb9b17a058273ee1519b2abbefba97cad7feb51b..29996864b35711b29629e9071d28493ac27fbde6 100644 ---- a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java -@@ -41,6 +41,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.EnchantingTableBlockEntity; -+import org.bukkit.craftbukkit.entity.CraftHumanEntity; -+// Purpur end -+ - public class EnchantmentMenu extends AbstractContainerMenu { - - static final ResourceLocation EMPTY_SLOT_LAPIS_LAZULI = ResourceLocation.withDefaultNamespace("container/slot/lapis_lazuli"); -@@ -75,6 +81,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 EnchantingTableBlockEntity enchantmentTable) { -+ enchantmentTable.setLapis(this.getItem(1).getCount()); -+ } -+ }); -+ } -+ } -+ // Purpur end - }; - this.random = RandomSource.create(); - this.enchantmentSeed = DataSlot.standalone(); -@@ -99,6 +121,16 @@ public class EnchantmentMenu extends AbstractContainerMenu { - return EnchantmentMenu.EMPTY_SLOT_LAPIS_LAZULI; - } - }); -+ // Purpur start -+ access.execute((level, pos) -> { -+ if (level.purpurConfig.enchantmentTableLapisPersists) { -+ BlockEntity blockEntity = level.getBlockEntity(pos); -+ if (blockEntity instanceof EnchantingTableBlockEntity enchantmentTable) { -+ this.getSlot(1).set(new ItemStack(Items.LAPIS_LAZULI, enchantmentTable.getLapis())); -+ } -+ } -+ }); -+ // Purpur end - this.addStandardInventorySlots(playerInventory, 8, 84); - this.addDataSlot(DataSlot.shared(this.costs, 0)); - this.addDataSlot(DataSlot.shared(this.costs, 1)); -@@ -328,6 +360,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/EnchantingTableBlock.java b/src/main/java/net/minecraft/world/level/block/EnchantingTableBlock.java -index 4c4e6290035710480cd5c1d7399f2443df01a5a6..248039ac7eab85b29ae3c525a986d91aa8d177fe 100644 ---- a/src/main/java/net/minecraft/world/level/block/EnchantingTableBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/EnchantingTableBlock.java -@@ -118,4 +118,18 @@ public class EnchantingTableBlock extends BaseEntityBlock { - protected boolean isPathfindable(BlockState state, 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 EnchantingTableBlockEntity enchantmentTable) { -+ net.minecraft.world.Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.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/EnchantingTableBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java -index 39aac959775afeaeea211f21d498cb0ddf0a3fcb..6349a342c023f378af431a73a62fb017882e257d 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java -@@ -28,6 +28,7 @@ public class EnchantingTableBlockEntity extends BlockEntity implements Nameable - private static final RandomSource RANDOM = RandomSource.create(); - @Nullable - private Component name; -+ private int lapis = 0; // Purpur - - public EnchantingTableBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.ENCHANTING_TABLE, pos, state); -@@ -39,6 +40,7 @@ public class EnchantingTableBlockEntity extends BlockEntity implements Nameable - if (this.hasCustomName()) { - nbt.putString("CustomName", Component.Serializer.toJson(this.name, registries)); - } -+ nbt.putInt("Purpur.Lapis", this.lapis); // Purpur - } - - @Override -@@ -47,6 +49,7 @@ public class EnchantingTableBlockEntity extends BlockEntity implements Nameable - if (nbt.contains("CustomName", 8)) { - this.name = parseCustomNameSafe(nbt.getString("CustomName"), registries); - } -+ this.lapis = nbt.getInt("Purpur.Lapis"); // Purpur - } - - public static void bookAnimationTick(Level world, BlockPos pos, BlockState state, EnchantingTableBlockEntity blockEntity) { -@@ -138,4 +141,14 @@ public class EnchantingTableBlockEntity extends BlockEntity implements Nameable - public void removeComponentsFromTag(CompoundTag nbt) { - nbt.remove("CustomName"); - } -+ -+ // 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 c122af4829a2edd3977af0310b27bb984d8eaad5..71a631b2adcd9f67ae8831a002129b6f76c3a331 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1565,6 +1565,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/0244-Option-to-disable-kick-for-out-of-order-chat.patch b/patches/server/0244-Option-to-disable-kick-for-out-of-order-chat.patch deleted file mode 100644 index 232030096d..0000000000 --- a/patches/server/0244-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/network/chat/SignedMessageChain.java b/src/main/java/net/minecraft/network/chat/SignedMessageChain.java -index 300929a406905f5ff1ede664d5b99fb0938d4d2e..a4e9ac0e07f08e0b6aa682e8c1587d9c84fc3c6c 100644 ---- a/src/main/java/net/minecraft/network/chat/SignedMessageChain.java -+++ b/src/main/java/net/minecraft/network/chat/SignedMessageChain.java -@@ -45,7 +45,7 @@ public class SignedMessageChain { - 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)) { - 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/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 4ad201f8457dca2305224cc74a1f55b90dd60140..4218aa563ba435a67c8e7af1fa9dc0c0d8d6b377 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -462,9 +462,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/0245-Config-for-sculk-shrieker-can_summon-state.patch b/patches/server/0245-Config-for-sculk-shrieker-can_summon-state.patch deleted file mode 100644 index 2def00ebe6..0000000000 --- a/patches/server/0245-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 7990a6c225c27845ccada0df91cf5ed7e4315a88..db2b21d5842fafa48dbde25a461505d03aa9b955 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 71a631b2adcd9f67ae8831a002129b6f76c3a331..5c0e0613c8e25f77b8ca92af09ef9b535e85995d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1032,6 +1032,11 @@ public class PurpurWorldConfig { - } - } - -+ 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); diff --git a/patches/server/0246-Config-to-not-let-coral-die.patch b/patches/server/0246-Config-to-not-let-coral-die.patch deleted file mode 100644 index a845f4ad68..0000000000 --- a/patches/server/0246-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 d7ca7a43d2d5f8cad416156fd40588cdd6634f52..920ad0a4ecc83da82c8382a48883cefb7a4b9d11 100644 ---- a/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BaseCoralPlantTypeBlock.java -@@ -39,6 +39,7 @@ public abstract class BaseCoralPlantTypeBlock extends Block implements SimpleWat - } - - protected static boolean scanForWater(BlockState state, BlockGetter world, BlockPos pos) { -+ if (!((net.minecraft.world.level.LevelAccessor) world).getMinecraftWorld().purpurConfig.coralDieOutsideWater) return true; // Purpur - Config to not let coral die - 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 a59b23f4062fa896836dec72cbd5097411774ad1..c87b90041825172afd079202241b7f9a206816c6 100644 ---- a/src/main/java/net/minecraft/world/level/block/CoralBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CoralBlock.java -@@ -60,6 +60,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 - Config to not let coral die - 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 5c0e0613c8e25f77b8ca92af09ef9b535e85995d..d80584a0412b6699c24f5817e8ec26cf9f46d92d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -881,6 +881,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/0247-Add-local-difficulty-api.patch b/patches/server/0247-Add-local-difficulty-api.patch deleted file mode 100644 index c84434e1e4..0000000000 --- a/patches/server/0247-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 92d9f0ea8f7810ae20d3996f49aefa539b4bcb69..f1e85d6c745397fe012f1ae0fd6f64353eac65a7 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -2374,6 +2374,12 @@ public class CraftWorld extends CraftRegionAccessor implements World { - 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(io.papermc.paper.util.MCUtil.toBlockPosition(location)).getEffectiveDifficulty(); -+ } -+ // Purpur end - Add local difficulty api -+ - @Override - public Collection getStructures(int x, int z) { - return this.getStructures(x, z, struct -> true); diff --git a/patches/server/0248-Add-toggle-for-RNG-manipulation.patch b/patches/server/0248-Add-toggle-for-RNG-manipulation.patch deleted file mode 100644 index 659c22c6f8..0000000000 --- a/patches/server/0248-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 25a6f228bad7deca7e7301868039d27bf65505c8..35cd273eb885558003a728eedc63f958e5accf74 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -599,7 +599,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - this.bb = Entity.INITIAL_AABB; - this.stuckSpeedMultiplier = Vec3.ZERO; - this.nextStep = 1.0F; -- this.random = SHARED_RANDOM; // Paper - Share random for entities to make them more random -+ this.random = world == null || world.purpurConfig.entitySharedRandom ? SHARED_RANDOM : RandomSource.create(); // Paper - Share random for entities to make them more random // 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 a2f51788f88c20f282ea2a20485c56109b90c22b..c7a7d1df79beb527ff94f876ca36a861c37c4947 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Squid.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java -@@ -46,7 +46,7 @@ public class Squid extends AgeableWaterCreature { - - public Squid(EntityType type, Level world) { - super(type, world); -- //this.random.setSeed((long)this.getId()); // Paper - Share random for entities to make them more random -+ if (!world.purpurConfig.entitySharedRandom) this.random.setSeed((long)this.getId()); // Paper - Share random for entities to make them more random // 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 d80584a0412b6699c24f5817e8ec26cf9f46d92d..596a4d0a7c6851c31ee2c09cfc11400683336e6d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -211,9 +211,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/0249-Add-more-logger-output-for-invalid-movement-kicks.patch b/patches/server/0249-Add-more-logger-output-for-invalid-movement-kicks.patch deleted file mode 100644 index 572e33eff5..0000000000 --- a/patches/server/0249-Add-more-logger-output-for-invalid-movement-kicks.patch +++ /dev/null @@ -1,35 +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 47952e504cc4d36b1d875651bb9bbd0a90606ae2..5b33034f01c05b29cd1a90847aa27b356a02264f 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -756,6 +756,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); - 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 - this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause - return; - } -@@ -1425,7 +1426,15 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - @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) { -+ 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 -+ // Purpur end - this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause - } else { - ServerLevel worldserver = this.player.serverLevel(); diff --git a/patches/server/0250-Add-Bee-API.patch b/patches/server/0250-Add-Bee-API.patch deleted file mode 100644 index 1478d48073..0000000000 --- a/patches/server/0250-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 19d1facb4173c11bb3a1d519603e4ec6906cdaa3..d2ac2c3a2481ee216a491333b173625da3881737 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -991,6 +991,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); -@@ -1034,6 +1035,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 -@@ -1083,6 +1085,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/0251-Debug-Marker-API.patch b/patches/server/0251-Debug-Marker-API.patch deleted file mode 100644 index 0da233ca62..0000000000 --- a/patches/server/0251-Debug-Marker-API.patch +++ /dev/null @@ -1,150 +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 9aa6744ab3a19e1ecf32b4aa059b7f4c555f03ff..b9f72ee3201e1d1d7d90df055ef4876f0bd27219 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1644,6 +1644,43 @@ public final class CraftServer implements Server { - 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) -> clearBlockHighlights()); -+ } -+ // Purpur end - Debug Marker API - - @Override - public List getRecipesFor(ItemStack result) { -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index f1e85d6c745397fe012f1ae0fd6f64353eac65a7..5d7af6c1ec557d2a2813b87a64b8c8a99d2f87e0 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -2379,6 +2379,43 @@ public class CraftWorld extends CraftRegionAccessor implements World { - return getHandle().getCurrentDifficultyAt(io.papermc.paper.util.MCUtil.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) { -+ 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 - Debug Marker API - - @Override - public Collection getStructures(int x, int z) { -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index da20ebe8858b4ab5bf8ac62aeac1942320bfa91c..95d011d48c312f247d766650b33d2366a774e5bf 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -3616,4 +3616,43 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - 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; -+ this.getHandle().connection.send(new net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket(new net.minecraft.network.protocol.common.custom.GameTestAddMarkerDebugPayload(io.papermc.paper.util.MCUtil.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; -+ this.getHandle().connection.send(new net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket(new net.minecraft.network.protocol.common.custom.GameTestClearMarkersDebugPayload())); -+ } -+ // Purpur end - Debug Marker API - } diff --git a/patches/server/0252-mob-spawning-option-to-ignore-creative-players.patch b/patches/server/0252-mob-spawning-option-to-ignore-creative-players.patch deleted file mode 100644 index 0f695e3fc8..0000000000 --- a/patches/server/0252-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 c1b76a1ebc1eea7ab70cf61d8175a31794dd122a..dc15c15951e4ca30b8341d24f813259a77f41c77 100644 ---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java -+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -280,7 +280,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 = world.getNearestPlayer(d0, (double) i, d1, -1.0D, false); -+ Player entityhuman = world.getNearestPlayer(d0, (double) i, d1, -1.0D, world.purpurConfig.mobSpawningIgnoreCreativePlayers); // 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 596a4d0a7c6851c31ee2c09cfc11400683336e6d..dee8860d593b126353f9f284de65b5fccbe4f530 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -380,6 +380,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); -@@ -388,6 +389,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/0253-Add-skeleton-bow-accuracy-option.patch b/patches/server/0253-Add-skeleton-bow-accuracy-option.patch deleted file mode 100644 index bc48af87d1..0000000000 --- a/patches/server/0253-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 20252b22c88d4cbfe7700052a8c56c9e0d703752..26b64d83b7466863b7340c3292494091e9fb89c1 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -+++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java -@@ -199,7 +199,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo - } - - if (event.getProjectile() == entityarrow.getBukkitEntity()) { -- Projectile.spawnProjectileUsingShoot(entityarrow, worldserver, itemstack1, d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float) (14 - worldserver.getDifficulty().getId() * 4)); -+ Projectile.spawnProjectileUsingShoot(entityarrow, worldserver, itemstack1, 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 end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index dee8860d593b126353f9f284de65b5fccbe4f530..5be4f4aebec0f2e78633d4849bc7ca032c2aef40 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2609,6 +2609,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); -@@ -2624,6 +2626,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 skeletonHorseRidable = false; diff --git a/patches/server/0254-Add-death-screen-API.patch b/patches/server/0254-Add-death-screen-API.patch deleted file mode 100644 index 82b325cc52..0000000000 --- a/patches/server/0254-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 95d011d48c312f247d766650b33d2366a774e5bf..df6fc6e6f4d1587dad704f609a854c5d996cf358 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -3655,4 +3655,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - 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 - Add 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 - Add death screen API - } diff --git a/patches/server/0255-Implement-ram-and-rambar-commands.patch b/patches/server/0255-Implement-ram-and-rambar-commands.patch deleted file mode 100644 index 85acd5e67b..0000000000 --- a/patches/server/0255-Implement-ram-and-rambar-commands.patch +++ /dev/null @@ -1,360 +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 5873f74df67d59f25682286a5b027963d8a6d382..93c07a4c96e25ed1db5e1f721ab5d53192a0225f 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -261,6 +261,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 - Implement ram and rambar commands -+ org.purpurmc.purpur.command.RamCommand.register(this.dispatcher); // Purpur - Implement ram and rambar commands - } - - 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 d97db4d05d7940d8408e7e0d473a3b17d956b858..6f39fd99ffb28d6c0267f4251c54af0c519289db 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -330,6 +330,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - public boolean purpurClient = false; // Purpur - Purpur client support - private boolean tpsBar = false; // Purpur - private boolean compassBar = false; // Purpur -+ private boolean ramBar = false; // Purpur - Implement ram and rambar commands - - // Paper start - rewrite chunk system - private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader; -@@ -694,6 +695,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - - if (nbt.contains("Purpur.TPSBar")) { this.tpsBar = nbt.getBoolean("Purpur.TPSBar"); } // Purpur - if (nbt.contains("Purpur.CompassBar")) { this.compassBar = nbt.getBoolean("Purpur.CompassBar"); } // Purpur -+ if (nbt.contains("Purpur.RamBar")) { this.ramBar = nbt.getBoolean("Purpur.RamBar"); } // Purpur - Implement ram and rambar commands - } - - @Override -@@ -746,6 +748,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - } - - this.saveEnderPearls(nbt); -+ nbt.putBoolean("Purpur.RamBar", this.ramBar); // Purpur - Implement ram and rambar commands - nbt.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur - nbt.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur - } -@@ -3457,5 +3460,13 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - public void compassBar(boolean compassBar) { - this.compassBar = compassBar; - } -+ -+ public boolean ramBar() { -+ return this.ramBar; -+ } -+ -+ public void ramBar(boolean ramBar) { -+ this.ramBar = ramBar; -+ } - // Purpur end - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 4218aa563ba435a67c8e7af1fa9dc0c0d8d6b377..dca02d697dd0982b006d4d975e7df745ab62dac5 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -185,6 +185,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 "; -@@ -202,6 +204,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); -@@ -250,6 +254,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; -@@ -277,6 +290,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 65c1bac3daf781c66442350ba08c43b21d2f1637..3c3d4cd52db93b97a40321030a70ebc282c9636b 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/0256-Configurable-block-blast-resistance.patch b/patches/server/0256-Configurable-block-blast-resistance.patch deleted file mode 100644 index 89b81960bf..0000000000 --- a/patches/server/0256-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 6376b8b3ff444f4cab93e2bb5d2becc77c33c118..c8ae6e4cd74549f753ec04def5d882de1ab72308 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 -@@ -91,7 +91,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 dca02d697dd0982b006d4d975e7df745ab62dac5..87ae04dff2645ba058325077cae4bcb20c7eab40 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -498,4 +498,19 @@ public class PurpurConfig { - String setPattern = getString("settings.username-valid-characters", defaultPattern); - usernameValidCharactersPattern = java.util.regex.Pattern.compile(setPattern == null || setPattern.isBlank() ? defaultPattern : setPattern); - } -+ -+ private static void blastResistanceSettings() { -+ getMap("settings.blast-resistance-overrides", Collections.emptyMap()).forEach((blockId, value) -> { -+ Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.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(); -+ }); -+ } - } diff --git a/patches/server/0257-Configurable-block-fall-damage-modifiers.patch b/patches/server/0257-Configurable-block-fall-damage-modifiers.patch deleted file mode 100644 index ed1d695c45..0000000000 --- a/patches/server/0257-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 db79ef24ea01db5f619ab29700d3d391438fb742..54836f2a1e4cc9046ba29fb71ea237b358c9cb7d 100644 ---- a/src/main/java/net/minecraft/world/level/block/BedBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BedBlock.java -@@ -183,7 +183,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 7531381e2e4d87ceeaa6478e13b23f6c072bef07..f00526753a83f95689fad2a132bef79f4479eec6 100644 ---- a/src/main/java/net/minecraft/world/level/block/Block.java -+++ b/src/main/java/net/minecraft/world/level/block/Block.java -@@ -88,6 +88,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 - Protect Bedrock and End Portal/Frames from being destroyed - public final boolean isDestroyable() { - return io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits || -@@ -473,7 +477,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 updateEntityMovementAfterFallOn(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 ef364aa171a48482a45bc18cfe730ec20c3f7be6..74971d90506aa253d5ee821b5390fb2551a3a393 100644 ---- a/src/main/java/net/minecraft/world/level/block/HayBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/HayBlock.java -@@ -23,6 +23,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 87ae04dff2645ba058325077cae4bcb20c7eab40..d32eaca6dbb258187086d11038f8c78c00b1778d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -513,4 +513,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.getValue(ResourceLocation.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(); -+ }); -+ } - } diff --git a/patches/server/0258-Language-API.patch b/patches/server/0258-Language-API.patch deleted file mode 100644 index 269543c15c..0000000000 --- a/patches/server/0258-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 b9f72ee3201e1d1d7d90df055ef4876f0bd27219..badf280a6b01b06e8148c552330872d64e6256b7 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -426,6 +426,20 @@ public final class CraftServer implements Server { - this.paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager); - this.pluginManager.paperPluginManager = this.paperPluginManager; - // Paper end -+ // 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 - - CraftRegistry.setMinecraftRegistry(console.registryAccess()); - diff --git a/patches/server/0259-Milk-Keeps-Beneficial-Effects.patch b/patches/server/0259-Milk-Keeps-Beneficial-Effects.patch deleted file mode 100644 index c87e8663dd..0000000000 --- a/patches/server/0259-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 b7607d917899823b0b95167a9dced495b7ae110f..4ec31b94803c15b7caad5053dc94b900a8601d07 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1149,6 +1149,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - - while (iterator.hasNext()) { - MobEffectInstance effect = iterator.next(); -+ if (cause == EntityPotionEffectEvent.Cause.MILK && !this.level().purpurConfig.milkClearsBeneficialEffects && effect.getEffect().value().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 89ad300d0ba9088af64fb9fee19399939bb4eff6..2dc7afa79126b52be42fc986926d6a63f9994d12 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java -@@ -126,7 +126,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 5be4f4aebec0f2e78633d4849bc7ca032c2aef40..464c38139175c51e64b55b615872cfdb235255a6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -145,6 +145,7 @@ public class PurpurWorldConfig { - public boolean persistentTileEntityLore = false; - public boolean persistentTileEntityDisplayName = true; - public int mobLastHurtByPlayerTime = 100; -+ public boolean milkClearsBeneficialEffects = true; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative); -@@ -178,6 +179,7 @@ public class PurpurWorldConfig { - 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); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0260-MC-121706-Fix-mobs-not-looking-up-and-down-when-stra.patch b/patches/server/0260-MC-121706-Fix-mobs-not-looking-up-and-down-when-stra.patch deleted file mode 100644 index e539f8454e..0000000000 --- a/patches/server/0260-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 515c1f671cb2c3a7cc23053aedf404bbbe77af3e..42a1e5b9c08a9245dd0ce6d4025e3bb5d60feb62 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 -@@ -116,9 +116,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/0261-Add-log-suppression-for-LibraryLoader.patch b/patches/server/0261-Add-log-suppression-for-LibraryLoader.patch deleted file mode 100644 index 303104ce45..0000000000 --- a/patches/server/0261-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 d32eaca6dbb258187086d11038f8c78c00b1778d..99cccf7c0e4928b47c2a43239475208003781d49 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -471,11 +471,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/0262-Add-an-option-to-fix-MC-3304-projectile-looting.patch b/patches/server/0262-Add-an-option-to-fix-MC-3304-projectile-looting.patch deleted file mode 100644 index 6ce4e9fff0..0000000000 --- a/patches/server/0262-Add-an-option-to-fix-MC-3304-projectile-looting.patch +++ /dev/null @@ -1,90 +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 d2a8e0fb1e673cec5701fd184cedfd8e49212acd..e136738ed53a488ad0aa67a04237ac6243fe712c 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -@@ -82,6 +82,7 @@ public abstract class AbstractArrow extends Projectile { - public ItemStack pickupItemStack; - @Nullable - public ItemStack firedFromWeapon; -+ 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 - - // Spigot Start - @Override -@@ -638,6 +639,12 @@ public abstract class AbstractArrow extends Projectile { - 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/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java b/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java -index 78ba170a83f8c026bd110eae494c52577182ed61..c2ae50872cead7202246b9cce4db6e0a81e1cf5f 100644 ---- a/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java -+++ b/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java -@@ -105,6 +105,8 @@ public abstract class ProjectileWeaponItem extends Item { - entityarrow.setCritArrow(true); - } - -+ entityarrow.setActualEnchantments(weaponStack.getEnchantments()); // Purpur - Add an option to fix MC-3304 projectile looting -+ - 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 810082126567eb02bec395065b95b3c3902d4973..be32255c9bd90f7de3f8f5a62d84c7dcf59fa722 100644 ---- a/src/main/java/net/minecraft/world/item/TridentItem.java -+++ b/src/main/java/net/minecraft/world/item/TridentItem.java -@@ -101,6 +101,9 @@ public class TridentItem extends Item implements ProjectileItem { - return false; - } - ThrownTrident entitythrowntrident = tridentDelayed.projectile(); // Paper - PlayerLaunchProjectileEvent -+ -+ entitythrowntrident.setActualEnchantments(stack.getEnchantments()); // Purpur - Add an option to fix MC-3304 projectile looting -+ - if (event.shouldConsume()) stack.hurtWithoutBreaking(1, entityhuman); // Paper - PlayerLaunchProjectileEvent - entitythrowntrident.pickupItemStack = stack.copy(); // SPIGOT-4511 update since damage call moved - // CraftBukkit end -diff --git a/src/main/java/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java b/src/main/java/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java -index 5f27e1ce23f2ed68e4c8af1986fafce940dbf826..d8cf49cbd82ed12d23fa10a81a88cc4bcf1c0f10 100644 ---- a/src/main/java/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java -+++ b/src/main/java/net/minecraft/world/level/storage/loot/functions/EnchantedCountIncreaseFunction.java -@@ -66,6 +66,11 @@ public class EnchantedCountIncreaseFunction extends LootItemConditionalFunction - Entity entity = context.getOptionalParameter(LootContextParams.ATTACKING_ENTITY); - if (entity instanceof LivingEntity livingEntity) { - int i = 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.AbstractArrow arrow) { -+ i = arrow.actualEnchantments.getLevel(this.enchantment); -+ } -+ // Purpur end - Add an option to fix MC-3304 projectile looting - if (i == 0) { - return stack; - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 99cccf7c0e4928b47c2a43239475208003781d49..8cbce0cb8fa811edf01af3dbdf69c8abd795b348 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -502,6 +502,11 @@ public class PurpurConfig { - usernameValidCharactersPattern = java.util.regex.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); -+ } -+ - private static void blastResistanceSettings() { - getMap("settings.blast-resistance-overrides", Collections.emptyMap()).forEach((blockId, value) -> { - Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.parse(blockId)); diff --git a/patches/server/0263-Add-option-to-allow-creeper-to-encircle-target-when-.patch b/patches/server/0263-Add-option-to-allow-creeper-to-encircle-target-when-.patch deleted file mode 100644 index e86528a2f7..0000000000 --- a/patches/server/0263-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 137ec75ee803789deb7b1ca93dd9369c9af362b9..ca95d25af3e9a0536868b0c7fd8e7d2ff1154ee3 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 464c38139175c51e64b55b615872cfdb235255a6..a50896fc423c9a1781c5d4b189395afeecb96756 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1448,6 +1448,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); -@@ -1467,6 +1468,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/0264-Fire-Immunity-API.patch b/patches/server/0264-Fire-Immunity-API.patch deleted file mode 100644 index de06422777..0000000000 --- a/patches/server/0264-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 35cd273eb885558003a728eedc63f958e5accf74..c12594a49435440a6c96039b2a54c1a3b1707fc0 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -390,6 +390,7 @@ 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 @Nullable Boolean immuneToFire = null; // Purpur - Fire immune API - - public void setOrigin(@javax.annotation.Nonnull Location location) { - this.origin = location.toVector(); -@@ -1980,7 +1981,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - - 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) { -@@ -2735,6 +2736,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - nbttagcompound.putBoolean("Paper.FreezeLock", true); - } - // Paper end -+ // Purpur start -+ if (immuneToFire != null) { -+ nbttagcompound.putBoolean("Purpur.FireImmune", immuneToFire); -+ } -+ // Purpur end - return nbttagcompound; - } catch (Throwable throwable) { - CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT"); -@@ -2885,6 +2891,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - 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 f9d7f1d317a9534d471a481db8c26f9d3b15bfda..0e1b3f64d1a828b9c69efe45c511582880bdcb92 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -@@ -88,6 +88,16 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { - } - - // Purpur start - API for any mob to burn daylight -+ @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 74f5b702b9602e4c8acbad4fb09c641e2c7844b2..762be3721419bfe33ea6577d3c6961204fdb0037 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -@@ -173,9 +173,14 @@ public class CraftItem extends CraftEntity implements Item { - return this.getHandle().immuneToExplosion; - } - -+ @Override -+ public void setImmuneToFire(@org.jetbrains.annotations.Nullable Boolean immuneToFire) { -+ this.getHandle().immuneToFire = (immuneToFire != null && immuneToFire); -+ } -+ - @Override - public void setImmuneToFire(boolean immuneToFire) { -- item.immuneToFire = immuneToFire; -+ this.setImmuneToFire((Boolean) immuneToFire); - } - - @Override diff --git a/patches/server/0265-Add-option-to-teleport-to-spawn-on-nether-ceiling-da.patch b/patches/server/0265-Add-option-to-teleport-to-spawn-on-nether-ceiling-da.patch deleted file mode 100644 index c4e5f70f03..0000000000 --- a/patches/server/0265-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 c12594a49435440a6c96039b2a54c1a3b1707fc0..c00a48fef499bc3b18a92010d88b32f10c839898 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1001,6 +1001,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - && 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(io.papermc.paper.util.MCUtil.toLocation(this.level, this.level.getSharedSpawnPos())); else // Purpur - Add option to teleport to spawn on nether ceiling damage - this.onBelowWorld(); - } - -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index a50896fc423c9a1781c5d4b189395afeecb96756..75d088219ab83ae9e6ca6b95f4551c917c2f79cd 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -420,6 +420,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; -@@ -447,6 +448,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/0266-Added-got-ram-event.patch b/patches/server/0266-Added-got-ram-event.patch deleted file mode 100644 index d7f7524f0e..0000000000 --- a/patches/server/0266-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 214a97aa88c569f55619512e712b7bfe32d26b30..54485641271a0570a4c6229eb1c5cb8ddff534b4 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 -@@ -435,6 +435,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/0267-Log-skipped-entity-s-position.patch b/patches/server/0267-Log-skipped-entity-s-position.patch deleted file mode 100644 index e2d39db9e1..0000000000 --- a/patches/server/0267-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 20a7fe2995db717f394fc3041435a95dbfee2ff7..2203410d3eac6362e949315b0ec15c270bdb5cb8 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityType.java -+++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -692,6 +692,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/0268-End-Crystal-Cramming.patch b/patches/server/0268-End-Crystal-Cramming.patch deleted file mode 100644 index 5b2e68b632..0000000000 --- a/patches/server/0268-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 218263958a90dfd95e4ecb7218c9eeefb069e50d..0967d13f26ee5bbe968322659ee3087685d43292 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 -@@ -102,6 +102,7 @@ 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 - } - - // Purpur start - Phantoms attracted to crystals and crystals shoot phantoms -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 75d088219ab83ae9e6ca6b95f4551c917c2f79cd..1400039583896ffe6198949e5e627ef58c5abee9 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -924,6 +924,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()))) { -@@ -951,6 +952,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/0269-Option-to-allow-beacon-effects-when-covered-by-tinte.patch b/patches/server/0269-Option-to-allow-beacon-effects-when-covered-by-tinte.patch deleted file mode 100644 index 3a06dee38b..0000000000 --- a/patches/server/0269-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 15a24cad5700cc1fe7430e425909869be84fe928..98e3d0a6f1be8cef8678b4048a3a484012092f08 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 -@@ -178,6 +178,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; -@@ -211,6 +212,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() >= 15 && !iblockdata1.is(Blocks.BEDROCK)) { - blockEntity.checkingBeamSections.clear(); - blockEntity.lastCheckY = l; -@@ -230,7 +234,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 - Custom beacon ranges - 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 1400039583896ffe6198949e5e627ef58c5abee9..0cc3e2a55712a809a3338b06ab23a3172334b1b3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -823,11 +823,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/0270-Add-attribute-clamping-and-armor-limit-config.patch b/patches/server/0270-Add-attribute-clamping-and-armor-limit-config.patch deleted file mode 100644 index 572284de50..0000000000 --- a/patches/server/0270-Add-attribute-clamping-and-armor-limit-config.patch +++ /dev/null @@ -1,61 +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 064c1e33f3feee77837bb57887877ae1ca39548d..ffd009bca3fdbfd0b14df78072ef8d472a57cd65 100644 ---- a/src/main/java/net/minecraft/world/damagesource/CombatRules.java -+++ b/src/main/java/net/minecraft/world/damagesource/CombatRules.java -@@ -15,7 +15,7 @@ public class CombatRules { - - public static float getDamageAfterAbsorb(LivingEntity armorWearer, float damageAmount, DamageSource damageSource, float armor, float armorToughness) { - float f = 2.0F + armorToughness / 4.0F; -- float g = Mth.clamp(armor - damageAmount / f, armor * 0.2F, 20.0F); -+ float g = Mth.clamp(armor - damageAmount / f, armor * 0.2F, org.purpurmc.purpur.PurpurConfig.limitArmor ? 20F : Float.MAX_VALUE); // Purpur - float h = g / 25.0F; - ItemStack itemStack = damageSource.getWeaponItem(); - float i; -@@ -30,7 +30,7 @@ public class CombatRules { - } - - 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 8cbce0cb8fa811edf01af3dbdf69c8abd795b348..535c7d6298ca62ea1bf808ac8deec1d2381b3831 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -507,6 +507,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.getValue(ResourceLocation.parse(blockId)); diff --git a/patches/server/0271-Config-to-remove-explosion-radius-clamp.patch b/patches/server/0271-Config-to-remove-explosion-radius-clamp.patch deleted file mode 100644 index 7d8a0a4eb7..0000000000 --- a/patches/server/0271-Config-to-remove-explosion-radius-clamp.patch +++ /dev/null @@ -1,44 +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/level/ServerExplosion.java b/src/main/java/net/minecraft/world/level/ServerExplosion.java -index 685ccfb73bf7125585ef90b6a0f51b2f81daa428..05fdb02b6f73c24f6985755effecf92c0b365cf0 100644 ---- a/src/main/java/net/minecraft/world/level/ServerExplosion.java -+++ b/src/main/java/net/minecraft/world/level/ServerExplosion.java -@@ -311,7 +311,7 @@ public class ServerExplosion implements Explosion { - public ServerExplosion(ServerLevel world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, Vec3 pos, float power, boolean createFire, Explosion.BlockInteraction destructionType) { - 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 - Config to remove explosion radius clamp - this.center = pos; - this.fire = createFire; - this.blockInteraction = destructionType; -@@ -666,7 +666,7 @@ public class ServerExplosion implements Explosion { - - public void explode() { - // CraftBukkit start -- if (this.radius < 0.1F) { -+ if ((this.level == null || this.level.purpurConfig.explosionClampRadius) && this.radius < 0.1F) { // Purpur - Config to remove explosion radius clamp - return; - } - // CraftBukkit end -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0cc3e2a55712a809a3338b06ab23a3172334b1b3..7c468d3002a0b6fca37ae5a731c5f0456af35744 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 { - infinityWorksWithoutArrows = getBoolean("gameplay-mechanics.infinity-bow.works-without-arrows", infinityWorksWithoutArrows); - } - -+ public boolean explosionClampRadius = true; -+ private void explosionSettings() { -+ explosionClampRadius = getBoolean("gameplay-mechanics.clamp-explosion-radius", explosionClampRadius); -+ } -+ - public List itemImmuneToCactus = new ArrayList<>(); - public List itemImmuneToExplosion = new ArrayList<>(); - public List itemImmuneToFire = new ArrayList<>(); diff --git a/patches/server/0272-bonemealable-sugarcane-cactus-and-netherwart.patch b/patches/server/0272-bonemealable-sugarcane-cactus-and-netherwart.patch deleted file mode 100644 index 472367957e..0000000000 --- a/patches/server/0272-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 bbfd8f5d404d0add94f0d8ac89a2964692b37e44..9f163ed07f8e6a5370c4c355b4e910f7a49b6bcd 100644 ---- a/src/main/java/net/minecraft/world/level/block/CactusBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/CactusBlock.java -@@ -24,7 +24,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 MapCodec CODEC = simpleCodec(CactusBlock::new); - public static final IntegerProperty AGE = BlockStateProperties.AGE_15; -@@ -135,4 +135,34 @@ public class CactusBlock extends Block { - protected boolean isPathfindable(BlockState state, PathComputationType type) { - return false; - } -+ -+ // Purpur start -+ @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 - } -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 b571bca4375ca7caf9b75dbf84009cb0604b66ad..264692baa4a20b66910d8ff379fa72acb99e27f8 100644 ---- a/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/NetherWartBlock.java -@@ -16,7 +16,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 MapCodec CODEC = simpleCodec(NetherWartBlock::new); - public static final int MAX_AGE = 3; -@@ -78,5 +78,22 @@ public class NetherWartBlock extends BushBlock { - super.playerDestroy(world, player, pos, state, blockEntity, itemInHand, includeDrops, dropExp); - } - } -+ -+ @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 - } -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 547ea09ed84595286c97c128b3b96f6d387ae25f..d0f8a13f27132257ece6dadf736c2dc6b1e5720e 100644 ---- a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java -@@ -20,7 +20,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 MapCodec CODEC = simpleCodec(SugarCaneBlock::new); - public static final IntegerProperty AGE = BlockStateProperties.AGE_15; -@@ -113,4 +113,34 @@ public class SugarCaneBlock extends Block { - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(SugarCaneBlock.AGE); - } -+ -+ // Purpur start -+ @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 - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 7c468d3002a0b6fca37ae5a731c5f0456af35744..280fae97ec04fea5ffe00189880a5de09c69dbc3 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -877,8 +877,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/0275-Make-GUI-Great-Again.patch b/patches/server/0275-Make-GUI-Great-Again.patch deleted file mode 100644 index 2a79d5d76c..0000000000 --- a/patches/server/0275-Make-GUI-Great-Again.patch +++ /dev/null @@ -1,421 +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 509e67eeab1f07ecce3bce0c8c222bc2c184ca6f..df035169ea89c4078e9a9226ac6e752f29d585a1 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -111,6 +111,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 = DedicatedServer.this.reader; -diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java -index 759062d219ff490a3cb19e710c4d18e3e08288e0..8f74c2ec5252b6265549589310d742337c91cb2c 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 - Improve ServerGUI -+ jframe.setName("Purpur Minecraft server"); // Paper - Improve ServerGUI // Purpur - - // Paper start - Improve ServerGUI - 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(); - } -@@ -159,7 +164,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); -@@ -171,10 +176,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() { // CraftBukkit - decompile error - public void focusGained(FocusEvent focusevent) {} - }); -@@ -210,7 +248,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); -@@ -224,11 +262,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); -@@ -236,4 +277,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..550222758bf0e7deff26a6e813a860b7be365e87 ---- /dev/null -+++ b/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/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..d75fb5e77eff27d86135ed7d605dbc250b660f7d ---- /dev/null -+++ b/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/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml -index d2a75850af9c6ad2aca66a5f994f1b587d73eac4..a056aa167887abef9e6d531a9edd2cda433567d2 100644 ---- a/src/main/resources/log4j2.xml -+++ b/src/main/resources/log4j2.xml -@@ -2,7 +2,16 @@ - - - -- -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - diff --git a/patches/server/0276-Stored-Bee-API.patch b/patches/server/0276-Stored-Bee-API.patch deleted file mode 100644 index 3bfebf3b69..0000000000 --- a/patches/server/0276-Stored-Bee-API.patch +++ /dev/null @@ -1,251 +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 7f0e37e23ff4c64355fdc822c0ac683959b8588a..fdebf45a27b903f4abb0bf55e1d79d78b7cc3d32 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 -@@ -147,11 +147,33 @@ public class BeehiveBlockEntity extends BlockEntity { - 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(); -@@ -467,9 +489,9 @@ public class BeehiveBlockEntity extends BlockEntity { - } - } - -- private static class BeeData { -+ public static class BeeData { // Purpur - change from private to public - Stored Bee API - -- private final BeehiveBlockEntity.Occupant occupant; -+ 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/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java -index 1a2a05160ba51d9c75f1ae6ae61d944d81428722..a86b026f2f420637d125cf697bcd07bf314c98aa 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 - Stored Bee API -+ - public CraftBeehive(World world, BeehiveBlockEntity tileEntity) { - super(world, tileEntity); -+ // Purpur start - load bees to be able to modify them individually - Stored Bee API -+ for(BeehiveBlockEntity.BeeData data : tileEntity.getStored()) { -+ storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(data, this)); -+ } -+ // Purpur end - Stored Bee API - } - - protected CraftBeehive(CraftBeehive state, Location location) { -@@ -76,14 +83,54 @@ public class CraftBeehive extends CraftBlockEntityState impl - } - } - -+ 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.getTileEntityFromWorld()); -+ 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)); -+ } -+ // Purpur end - Stored Bee API - } - - @Override -@@ -100,6 +147,7 @@ public class CraftBeehive extends CraftBlockEntityState impl - @Override - public void clearEntities() { - getSnapshot().clearBees(); -+ storage.clear(); // Purpur - Stored Bee API - } - // 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..7608bf0981fa0d37031e51e57e4086cb5ec4c88b ---- /dev/null -+++ b/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java -@@ -0,0 +1,106 @@ -+package org.purpurmc.purpur.entity; -+ -+import io.papermc.paper.adventure.PaperAdventure; -+import net.kyori.adventure.text.Component; -+import net.minecraft.nbt.CompoundTag; -+import net.minecraft.nbt.Tag; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.world.item.component.CustomData; -+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; -+ -+ CompoundTag customData = handle.occupant.entityData().copyTag(); -+ this.customName = customData.contains("CustomName") -+ ? PaperAdventure.asAdventure(net.minecraft.network.chat.Component.Serializer.fromJson(customData.getString("CustomName"), MinecraftServer.getDefaultRegistryAccess())) -+ : null; -+ -+ if(customData.contains("BukkitValues", Tag.TAG_COMPOUND)) { -+ this.persistentDataContainer.putAll(customData.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.occupant.entityData().copyTag().put("BukkitValues", this.persistentDataContainer.toTagCompound()); -+ if(customName == null) { -+ handle.occupant.entityData().copyTag().remove("CustomName"); -+ } else { -+ handle.occupant.entityData().copyTag().putString("CustomName", net.minecraft.network.chat.Component.Serializer.toJson(PaperAdventure.asVanilla(customName), MinecraftServer.getDefaultRegistryAccess())); -+ } -+ } -+} diff --git a/patches/server/0277-Shears-can-defuse-TNT.patch b/patches/server/0277-Shears-can-defuse-TNT.patch deleted file mode 100644 index 9e953e2f13..0000000000 --- a/patches/server/0277-Shears-can-defuse-TNT.patch +++ /dev/null @@ -1,61 +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 809f5e847e2f5bb594c130cebd2cb897ea768d82..6f1e21d6c104d71fe4fc3376ed2f2273a5f3d3cc 100644 ---- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -+++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -249,4 +249,31 @@ public class PrimedTnt extends Entity implements TraceableEntity { - return !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, LivingEntity.getSlotForHand(hand)); -+ 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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 280fae97ec04fea5ffe00189880a5de09c69dbc3..9847f58a029901a765df3809b24d56b8e2e50f02 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -3423,4 +3423,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/0278-Explorer-Map-API.patch b/patches/server/0278-Explorer-Map-API.patch deleted file mode 100644 index 411627d30e..0000000000 --- a/patches/server/0278-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 8ff50a4c7461bbd9f469d503f6b5ee482d2463d7..5c0a46c11003b6e154195a8ef299416cc73eae33 100644 ---- a/src/main/java/net/minecraft/world/item/MapItem.java -+++ b/src/main/java/net/minecraft/world/item/MapItem.java -@@ -194,6 +194,7 @@ public class MapItem extends Item { - public static void renderBiomePreviewMap(ServerLevel world, ItemStack map) { - MapItemSavedData mapItemSavedData = getSavedData(map, world); - if (mapItemSavedData != null) { -+ mapItemSavedData.isExplorerMap = true; // Purpur - if (world.dimension() == mapItemSavedData.dimension) { - int i = 1 << mapItemSavedData.scale; - int j = mapItemSavedData.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 ae321b3b8d98e42ef07fd1f0f738c1a2b428f6db..26da9e7c25ef6a89482838010d8ed6bcf8c87511 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 -@@ -81,6 +81,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 cf0920e5f84b35647882fb963e9972af4e8427e0..e30c851acf49a425cd4fd409a6d5bbb2ff836e0e 100644 ---- a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java -+++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java -@@ -49,4 +49,10 @@ public class CraftMapRenderer extends MapRenderer { - } - } - -+ // Purpur - start -+ @Override -+ public boolean isExplorerMap() { -+ return this.worldMap.isExplorerMap; -+ } -+ // Purpur - end - } diff --git a/patches/server/0279-Option-Ocelot-Spawn-Under-Sea-Level.patch b/patches/server/0279-Option-Ocelot-Spawn-Under-Sea-Level.patch deleted file mode 100644 index 8bf0fbdf82..0000000000 --- a/patches/server/0279-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 be818af35d6e9495ce4d3d3fc8212f7a22ebae92..79db05362cddbef624e5f5e19e11faa951b6f81c 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java -@@ -288,7 +288,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 9847f58a029901a765df3809b24d56b8e2e50f02..8031960ec1d83a26cac92f6f01d76b9e328624e2 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2159,6 +2159,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); -@@ -2173,6 +2174,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/0280-add-an-option-for-piglins-to-ignore-gold-trimmed-arm.patch b/patches/server/0280-add-an-option-for-piglins-to-ignore-gold-trimmed-arm.patch deleted file mode 100644 index 4fa363448f..0000000000 --- a/patches/server/0280-add-an-option-for-piglins-to-ignore-gold-trimmed-arm.patch +++ /dev/null @@ -1,50 +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 e283b1296c1e831376bfe9491cbf02ed4b3fffe4..27a6de70530c2a1cbe2f77a7fb493038121710ea 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 -@@ -605,11 +605,18 @@ public class PiglinAi { - } - - itemstack = (ItemStack) iterator.next(); -- } while (!itemstack.is(ItemTags.PIGLIN_SAFE_ARMOR)); -+ } while (!itemstack.is(ItemTags.PIGLIN_SAFE_ARMOR) && (!entity.level().purpurConfig.piglinIgnoresArmorWithGoldTrim || !isWearingGoldTrim(itemstack.getItem()))); // Purpur - - return true; - } - -+ // Purpur start -+ private static boolean isWearingGoldTrim(Item itemstack) { -+ net.minecraft.world.item.equipment.trim.ArmorTrim armorTrim = itemstack.components().get(net.minecraft.core.component.DataComponents.TRIM); -+ return armorTrim != null && armorTrim.material().is(net.minecraft.world.item.equipment.trim.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 8031960ec1d83a26cac92f6f01d76b9e328624e2..3308e2ce5bf49996c7dd75bb604989225cabd16d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2336,6 +2336,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); -@@ -2352,6 +2353,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/0281-Add-option-for-always-showing-item-in-player-death-m.patch b/patches/server/0281-Add-option-for-always-showing-item-in-player-death-m.patch deleted file mode 100644 index 36c075bb46..0000000000 --- a/patches/server/0281-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 10afc08930d7fe27ffa396ec5a10afb7769a3f0b..6853b91e047b92ae4042a26d6aad84874be11709 100644 ---- a/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -+++ b/src/main/java/net/minecraft/world/damagesource/CombatTracker.java -@@ -54,7 +54,7 @@ public class CombatTracker { - - private Component getMessageForAssistedFall(Entity attacker, Component attackerDisplayName, String itemDeathTranslationKey, String deathTranslationKey) { - ItemStack itemStack = attacker 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 - ? Component.translatable(itemDeathTranslationKey, this.mob.getDisplayName(), attackerDisplayName, itemStack.getDisplayName()) - : Component.translatable(deathTranslationKey, this.mob.getDisplayName(), attackerDisplayName); - } -diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -index ccdf9d40b767115e0e6db2c29af42f5ec4c40f85..3781bca61379516d537650c79c614933454fdcd8 100644 ---- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java -+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java -@@ -219,7 +219,7 @@ public class DamageSource { - - ItemStack itemstack1 = itemstack; - -- return !itemstack1.isEmpty() && itemstack1.has(DataComponents.CUSTOM_NAME) ? Component.translatable(s + ".item", killed.getDisplayName(), ichatbasecomponent, itemstack1.getDisplayName()) : Component.translatable(s, killed.getDisplayName(), ichatbasecomponent); -+ return !itemstack1.isEmpty() && (org.purpurmc.purpur.PurpurConfig.playerDeathsAlwaysShowItem || itemstack1.has(DataComponents.CUSTOM_NAME)) ? 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 535c7d6298ca62ea1bf808ac8deec1d2381b3831..ab672b9041ae40ba3e78bc5f9f465ee70e420993 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -577,4 +577,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/0282-place-end-crystal-on-any-block.patch b/patches/server/0282-place-end-crystal-on-any-block.patch deleted file mode 100644 index 3000e2897b..0000000000 --- a/patches/server/0282-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 b62db8c7c8c57e43869ee239ebf4b02f112355d9..f60e39e56a5dab2de62ae9cfd7a30a70b4985c09 100644 ---- a/src/main/java/net/minecraft/world/item/EndCrystalItem.java -+++ b/src/main/java/net/minecraft/world/item/EndCrystalItem.java -@@ -27,7 +27,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 3308e2ce5bf49996c7dd75bb604989225cabd16d..552993124deefef3b9875608cea545b3a97b8bc6 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -944,6 +944,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()))) { -@@ -972,6 +973,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/0283-Add-option-to-disable-the-copper-oxidation-proximity.patch b/patches/server/0283-Add-option-to-disable-the-copper-oxidation-proximity.patch deleted file mode 100644 index 46c6e1a715..0000000000 --- a/patches/server/0283-Add-option-to-disable-the-copper-oxidation-proximity.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Meln Cat -Date: Mon, 9 Oct 2023 12:21:49 -0700 -Subject: [PATCH] Add option to disable the copper oxidation proximity penalty - - -diff --git a/src/main/java/net/minecraft/world/level/block/ChangeOverTimeBlock.java b/src/main/java/net/minecraft/world/level/block/ChangeOverTimeBlock.java -index daae7fd6e0148cfba8e359d990748a0c83a3376e..0e06b1bcd906e92c083dc74d56d6d0a2a36f62a7 100644 ---- a/src/main/java/net/minecraft/world/level/block/ChangeOverTimeBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/ChangeOverTimeBlock.java -@@ -67,7 +67,7 @@ public interface ChangeOverTimeBlock> { - } - - float f = (float) (k + 1) / (float) (k + j + 1); -- float f1 = f * f * this.getChanceModifier(); -+ float f1 = world.purpurConfig.disableOxidationProximityPenalty ? this.getChanceModifier() : f * f * this.getChanceModifier(); // Purpur - - return random.nextFloat() < f1 ? this.getNext(state) : Optional.empty(); - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 552993124deefef3b9875608cea545b3a97b8bc6..b59e74ec85573ced20a47f0f732608d057511e2d 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -146,6 +146,7 @@ public class PurpurWorldConfig { - public boolean persistentTileEntityDisplayName = true; - public int mobLastHurtByPlayerTime = 100; - public boolean milkClearsBeneficialEffects = true; -+ public boolean disableOxidationProximityPenalty = false; - private void miscGameplayMechanicsSettings() { - useBetterMending = getBoolean("gameplay-mechanics.use-better-mending", useBetterMending); - alwaysTameInCreative = getBoolean("gameplay-mechanics.always-tame-in-creative", alwaysTameInCreative); -@@ -180,6 +181,7 @@ public class PurpurWorldConfig { - 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); - } - - public int daytimeTicks = 12000; diff --git a/patches/server/0284-register-minecraft-debug-commands.patch b/patches/server/0284-register-minecraft-debug-commands.patch deleted file mode 100644 index 11a7ee3694..0000000000 --- a/patches/server/0284-register-minecraft-debug-commands.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Sun, 18 Feb 2024 16:28:32 -0800 -Subject: [PATCH] register minecraft debug commands - - -diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index 93c07a4c96e25ed1db5e1f721ab5d53192a0225f..fe9a01e19ef182fb8e9c653fc1232ec7f13037e4 100644 ---- a/src/main/java/net/minecraft/commands/Commands.java -+++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -226,8 +226,8 @@ public class Commands { - JfrCommand.register(this.dispatcher); - } - -- if (SharedConstants.IS_RUNNING_IN_IDE) { -- TestCommand.register(this.dispatcher); -+ if (org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands || SharedConstants.IS_RUNNING_IN_IDE) { // Purpur -+ if (!org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands) TestCommand.register(this.dispatcher); // Purpur - RaidCommand.register(this.dispatcher, commandRegistryAccess); - DebugPathCommand.register(this.dispatcher); - DebugMobSpawningCommand.register(this.dispatcher); -diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java -index 978c0d7296f400fe2ebda89e4f61386e6e87fe0c..a880f4e5cf712654649ad043e58e073e9a87c0fe 100644 ---- a/src/main/java/net/minecraft/server/Main.java -+++ b/src/main/java/net/minecraft/server/Main.java -@@ -125,6 +125,7 @@ public class Main { - // Purpur start - 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 end - load config files early - - io.papermc.paper.plugin.PluginInitializerManager.load(optionset); // Paper -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index ab672b9041ae40ba3e78bc5f9f465ee70e420993..2d76827f83638c7f6bfa0d45bf950e18df22063e 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -582,4 +582,9 @@ public class PurpurConfig { - 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); -+ } - } diff --git a/patches/server/0285-Configurable-villager-search-radius.patch b/patches/server/0285-Configurable-villager-search-radius.patch deleted file mode 100644 index 67ab67b63c..0000000000 --- a/patches/server/0285-Configurable-villager-search-radius.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Pantera -Date: Fri, 26 Jan 2024 15:57:24 +0900 -Subject: [PATCH] Configurable-villager-search-radius - - -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 0d177e828c2b338ce93c58aaef04df326e1eb0b2..273ba657926ce72a7c82861e880a82bf7f322a0b 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 -@@ -86,7 +86,7 @@ public class AcquirePoi { - }; - // Paper start - optimise POI access - final java.util.List, BlockPos>> poiposes = new java.util.ArrayList<>(); -- io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, poiPredicate, predicate2, entity.blockPosition(), 48, 48*48, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes); -+ io.papermc.paper.util.PoiAccess.findNearestPoiPositions(poiManager, poiPredicate, predicate2, entity.blockPosition(), world.purpurConfig.villagerAcquirePoiSearchRadius, world.purpurConfig.villagerAcquirePoiSearchRadius*world.purpurConfig.villagerAcquirePoiSearchRadius, PoiManager.Occupancy.HAS_SPACE, false, 5, poiposes); - final Set, BlockPos>> set = new java.util.HashSet<>(poiposes.size()); - for (final Pair, BlockPos> poiPose : poiposes) { - if (worldPosBiPredicate.test(world, poiPose.getSecond())) { -diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -index 92731b6b593289e9f583c9b705b219e81fcd8e73..9104d7010bda6f9f73b478c11490ef9c53f76da2 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -@@ -56,7 +56,7 @@ public class NearestBedSensor extends Sensor { - // 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(), world.purpurConfig.villagerNearestBedSensorSearchRadius, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes); // Purpur - Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes)); - // Paper end - optimise POI access - if (path != null && path.canReach()) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b59e74ec85573ced20a47f0f732608d057511e2d..eb3d81c9eb9a332fd39382d28d022395d9aa4390 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -3013,6 +3013,8 @@ public class PurpurWorldConfig { - 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); -@@ -3051,6 +3053,8 @@ public class PurpurWorldConfig { - 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; diff --git a/patches/server/0286-option-to-make-ravagers-afraid-of-rabbits.patch b/patches/server/0286-option-to-make-ravagers-afraid-of-rabbits.patch deleted file mode 100644 index 4d08149302..0000000000 --- a/patches/server/0286-option-to-make-ravagers-afraid-of-rabbits.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Tue, 7 May 2024 04:59:57 -0700 -Subject: [PATCH] option to make ravagers afraid of rabbits - -https://github.com/PurpurMC/Purpur/discussions/713 - -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 95562e3614880702655df4578119e6cf3702e566..a5134a80a707bc69ae5826eae89e79be5ec3fa41 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java -@@ -122,6 +122,7 @@ public class Ravager extends Raider { - super.registerGoals(); - this.goalSelector.addGoal(0, new FloatGoal(this)); - this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables -+ if (level().purpurConfig.ravagerAvoidRabbits) this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.AvoidEntityGoal<>(this, net.minecraft.world.entity.animal.Rabbit.class, 6.0F, 1.0D, 1.2D)); // Purpur - option to make ravagers afraid of rabbits - this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, true)); - this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.4D)); - 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 eb3d81c9eb9a332fd39382d28d022395d9aa4390..0d5a0e14cbaacc63eeced78a6c28cc64ad918522 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -2494,6 +2494,7 @@ public class PurpurWorldConfig { - 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); -@@ -2525,6 +2526,7 @@ public class PurpurWorldConfig { - } - }); - ravagerAlwaysDropExp = getBoolean("mobs.ravager.always-drop-exp", ravagerAlwaysDropExp); -+ ravagerAvoidRabbits = getBoolean("mobs.ravager.avoid-rabbits", ravagerAvoidRabbits); - } - - public boolean salmonRidable = false; diff --git a/patches/server/0287-config-for-startup-commands.patch b/patches/server/0287-config-for-startup-commands.patch deleted file mode 100644 index b1ee061636..0000000000 --- a/patches/server/0287-config-for-startup-commands.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Sun, 5 May 2024 02:27:52 -0700 -Subject: [PATCH] config for startup commands - - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 1dc4476ca1fc41030001d4d23ffff1b810a056cd..115069f2bce9b7742d8d3fbf181a47cacf8b0046 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1293,6 +1293,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 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/patches/server/0288-Config-to-reverse-bubble-column-flow.patch b/patches/server/0288-Config-to-reverse-bubble-column-flow.patch deleted file mode 100644 index 221d084f14..0000000000 --- a/patches/server/0288-Config-to-reverse-bubble-column-flow.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Sat, 8 Jun 2024 22:06:47 -0700 -Subject: [PATCH] Config to reverse bubble column flow - - -diff --git a/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java b/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java -index 385da0585f409ee453f10d45f5837cdc09adc21b..c65016cba376a41c267fb4b6499ec0a263851558 100644 ---- a/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java -@@ -123,10 +123,10 @@ public class BubbleColumnBlock extends Block implements BucketPickup { - if (state.is(Blocks.BUBBLE_COLUMN)) { - return state; - } else if (state.is(Blocks.SOUL_SAND)) { -- return Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(false)); -+ return Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(org.purpurmc.purpur.PurpurConfig.soulSandBlockReverseBubbleColumnFlow)); // Purpur - } else { - return state.is(Blocks.MAGMA_BLOCK) -- ? Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(true)) -+ ? Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(!org.purpurmc.purpur.PurpurConfig.magmaBlockReverseBubbleColumnFlow)) // Purpur - : Blocks.WATER.defaultBlockState(); - } - } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 66953d206d59c6e8ef3f925aca7451d57f98b81a..7690441b5059ae6c7ca8519875ea8a515c5c5e93 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -344,6 +344,8 @@ public class PurpurConfig { - 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); -@@ -417,6 +419,8 @@ public class PurpurConfig { - 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; diff --git a/patches/server/0290-Fire-EntityTeleportHinderedEvent-when-attempting-to-.patch b/patches/server/0290-Fire-EntityTeleportHinderedEvent-when-attempting-to-.patch deleted file mode 100644 index eb80c84680..0000000000 --- a/patches/server/0290-Fire-EntityTeleportHinderedEvent-when-attempting-to-.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Villagers654 <110007851+Villagers654@users.noreply.github.com> -Date: Mon, 22 Jul 2024 21:03:09 -0400 -Subject: [PATCH] Fire EntityTeleportHinderedEvent when attempting to teleport - a player with passengers - - -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index df6fc6e6f4d1587dad704f609a854c5d996cf358..c2df3c38f58d8dcb5e3d62077655af56a3bffd65 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -1471,6 +1471,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - } - - if (entity.isVehicle() && !ignorePassengers) { // Paper - Teleport API -+ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur start - return false; - } - diff --git a/purpur-api-generator/paper-patches/files/generated/com/destroystokyo/paper/entity/ai/VanillaGoal.java.patch b/purpur-api-generator/paper-patches/files/generated/com/destroystokyo/paper/entity/ai/VanillaGoal.java.patch new file mode 100644 index 0000000000..0173a42d9b --- /dev/null +++ b/purpur-api-generator/paper-patches/files/generated/com/destroystokyo/paper/entity/ai/VanillaGoal.java.patch @@ -0,0 +1,29 @@ +--- a/generated/com/destroystokyo/paper/entity/ai/VanillaGoal.java ++++ b/generated/com/destroystokyo/paper/entity/ai/VanillaGoal.java +@@ -441,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/build.gradle.kts.patch b/purpur-api/build.gradle.kts.patch new file mode 100644 index 0000000000..3f880d6058 --- /dev/null +++ b/purpur-api/build.gradle.kts.patch @@ -0,0 +1,47 @@ +--- a/paper-api/build.gradle.kts ++++ b/paper-api/build.gradle.kts +@@ -103,6 +_,18 @@ + main { + java { + srcDir(generatedApiPath) ++ 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")) + } + } + } +@@ -168,8 +_,10 @@ + val services = objects.newInstance() + + 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.overview = "../paper-api/src/main/javadoc/overview.html" + options.use() + options.isDocFilesSubDirs = true + options.links( +@@ -202,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/patches/api/0001-Rebrand.patch b/purpur-api/paper-patches/features/0001-Rebrand.patch similarity index 100% rename from patches/api/0001-Rebrand.patch rename to purpur-api/paper-patches/features/0001-Rebrand.patch diff --git a/patches/api/0002-Purpur-config-files.patch b/purpur-api/paper-patches/features/0002-Purpur-config-files.patch similarity index 85% rename from patches/api/0002-Purpur-config-files.patch rename to purpur-api/paper-patches/features/0002-Purpur-config-files.patch index 468f177146..e3535a3989 100644 --- a/patches/api/0002-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 ad816538b30079c62d5e1eb98c6f4b61e12e8d47..f90da51a8d1003a5cba86decbd42470f7f7e9211 100644 +index 365368983a25f7ccbd3c8b7b572a5173a4c868a0..594bcedd823acc87ed429ad8ef17b66e9dc15beb 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java -@@ -2283,6 +2283,18 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi +@@ -2330,6 +2330,18 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi } // Paper end 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 0000000000..382cc79152 --- /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 0000000000..d971ba0c25 --- /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 0000000000..beab0b68d6 --- /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 0000000000..96b13d99c4 --- /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 0000000000..4264eea116 --- /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 +@@ -28,6 +_,12 @@ + */ + Component getVersionMessage(String serverVersion); + ++ // Purpur start ++ default int distance() { ++ return 0; ++ } ++ // Purpur end ++ + @ApiStatus.Internal + class DummyVersionFetcher implements VersionFetcher { + 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 0000000000..7bdf2cf76e --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/Bukkit.java.patch @@ -0,0 +1,136 @@ +--- a/src/main/java/org/bukkit/Bukkit.java ++++ b/src/main/java/org/bukkit/Bukkit.java +@@ -2980,4 +_,133 @@ + public static Server.Spigot spigot() { + return server.spigot(); + } ++ ++ // 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 ++ */ ++ 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 - Debug Marker API + } diff --git a/patches/api/0016-ChatColor-conveniences.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/ChatColor.java.patch similarity index 85% rename from patches/api/0016-ChatColor-conveniences.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/ChatColor.java.patch index bf0bd59b42..a9da296b06 100644 --- a/patches/api/0016-ChatColor-conveniences.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/ChatColor.java.patch @@ -1,19 +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..e98d6321c5f2cdde91b54f8a74cbcc045eae75a8 100644 --- a/src/main/java/org/bukkit/ChatColor.java +++ b/src/main/java/org/bukkit/ChatColor.java -@@ -456,4 +456,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 + * @@ -84,5 +76,5 @@ index 918a045165cdcde264bc24082b7afebb407271de..e98d6321c5f2cdde91b54f8a74cbcc04 + 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 0000000000..48bef86e48 --- /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 +@@ -5812,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/0029-Extended-OfflinePlayer-API.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/OfflinePlayer.java.patch similarity index 88% rename from patches/api/0029-Extended-OfflinePlayer-API.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/OfflinePlayer.java.patch index 932732b9a6..5048818d24 100644 --- a/patches/api/0029-Extended-OfflinePlayer-API.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/OfflinePlayer.java.patch @@ -1,14 +1,6 @@ -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 5622fe3165baad8138c22cfc016ed6c3834cf702..6d31b561d915180fcd473b317721064feed28f37 100644 --- a/src/main/java/org/bukkit/OfflinePlayer.java +++ b/src/main/java/org/bukkit/OfflinePlayer.java -@@ -573,4 +573,106 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio +@@ -573,4 +_,106 @@ @Override io.papermc.paper.persistence.@NotNull PersistentDataContainerView getPersistentDataContainer(); // Paper end - add pdc to offline player 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 0000000000..b149d0c27d --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/Server.java.patch @@ -0,0 +1,114 @@ +--- a/src/main/java/org/bukkit/Server.java ++++ b/src/main/java/org/bukkit/Server.java +@@ -2654,4 +_,111 @@ + */ + 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 ++ */ ++ 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 - 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 0000000000..2fb8c14984 --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/World.java.patch @@ -0,0 +1,89 @@ +--- a/src/main/java/org/bukkit/World.java ++++ b/src/main/java/org/bukkit/World.java +@@ -4244,6 +_,86 @@ + @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 ++ */ ++ 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 ++ + /** + * 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 0000000000..94b62abaa9 --- /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 0000000000..83f75df210 --- /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 +@@ -153,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); +@@ -160,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/patches/api/0028-Clean-up-version-command-output.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/command/defaults/VersionCommand.java.patch similarity index 51% rename from patches/api/0028-Clean-up-version-command-output.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/command/defaults/VersionCommand.java.patch index 3bbcebb142..b424f2eb99 100644 --- a/patches/api/0028-Clean-up-version-command-output.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/command/defaults/VersionCommand.java.patch @@ -1,31 +1,6 @@ -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 023cc52a9e28e1238c7452c0f3f577f2850fd861..00b3f46ddd26ae08744d3dba211f92624d4b1063 100644 ---- a/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java -+++ b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java -@@ -28,6 +28,12 @@ public interface VersionFetcher { - */ - Component getVersionMessage(String serverVersion); - -+ // Purpur start -+ default int distance() { -+ return 0; -+ } -+ // Purpur end -+ - @ApiStatus.Internal - class DummyVersionFetcher implements VersionFetcher { - -diff --git a/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/src/main/java/org/bukkit/command/defaults/VersionCommand.java -index e64bb57f74e6d6f78927be228825b3e0bdf41f48..c880d0010849ab733ad13bbd18fab3c864d0cf61 100644 --- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java +++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java -@@ -215,7 +215,7 @@ public class VersionCommand extends BukkitCommand { +@@ -215,7 +_,7 @@ String version = Bukkit.getVersion(); // Paper start if (version.startsWith("null")) { // running from ide? @@ -34,7 +9,7 @@ index e64bb57f74e6d6f78927be228825b3e0bdf41f48..c880d0010849ab733ad13bbd18fab3c8 return; } setVersionMessage(getVersionFetcher().getVersionMessage(version)); -@@ -256,9 +256,11 @@ public class VersionCommand extends BukkitCommand { +@@ -256,9 +_,11 @@ // Paper start private void setVersionMessage(final @NotNull Component msg) { lastCheck = System.currentTimeMillis(); diff --git a/patches/api/0023-Add-enchantment-target-for-bows-and-crossbows.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java.patch similarity index 53% rename from patches/api/0023-Add-enchantment-target-for-bows-and-crossbows.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java.patch index ecbfea9ea2..45b679507c 100644 --- a/patches/api/0023-Add-enchantment-target-for-bows-and-crossbows.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/enchantments/EnchantmentTarget.java.patch @@ -1,18 +1,10 @@ -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 6fcc15d588239481136876d117ab346a8deac1dd..bd653ad99e8e75af1494b595640c3910f4d37e6e 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 { +@@ -227,6 +_,30 @@ public boolean includes(@NotNull Material item) { return BREAKABLE.includes(item) || (WEARABLE.includes(item) && !item.equals(Material.ELYTRA)) || item.equals(Material.COMPASS); } -+ // Purpur start ++ // Purpur start - Add enchantment target for bows and crossbows + }, + + /** @@ -23,7 +15,19 @@ index 6fcc15d588239481136876d117ab346a8deac1dd..bd653ad99e8e75af1494b595640c3910 + public boolean includes(@NotNull Material item) { + return item.equals(Material.BOW) || item.equals(Material.CROSSBOW); + } -+ // Purpur end ++ // 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/patches/api/0025-Add-back-player-spawned-endermite-API.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Endermite.java.patch similarity index 72% rename from patches/api/0025-Add-back-player-spawned-endermite-API.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Endermite.java.patch index bb53255c68..218b3659e0 100644 --- a/patches/api/0025-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 7b379fb21e800a766ad022705a12dff6d42279ab..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 { /** 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 0000000000..2ee29df0c3 --- /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 +@@ -1196,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 0000000000..8a25a65b22 --- /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/0017-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/0017-Item-entity-immunities.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Item.java.patch index dd689e3cd8..294924ed9b 100644 --- a/patches/api/0017-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 bcc6ba95bd21c7972865838c636a03f50b6c1f1a..c3fcd8dd7dbb1e1a18e17c014c1e641149ea5960 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 0000000000..2ec452396c --- /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 +@@ -1468,4 +_,20 @@ + */ + boolean canUseEquipmentSlot(org.bukkit.inventory.@NotNull EquipmentSlot slot); + // Paper end - Expose canUseSlot ++ ++ // 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 0000000000..43e170b4ba --- /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 0000000000..9887a20d82 --- /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 +@@ -3892,4 +_,123 @@ + */ + void sendEntityEffect(org.bukkit.@NotNull EntityEffect effect, @NotNull Entity target); + // Paper end - entity effect API ++ ++ // Purpur start ++ /** ++ * Allows you to get if player uses PurpurClient ++ * ++ * @return true if player uses PurpurClient ++ */ ++ 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 ++ * @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(@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(); ++ ++ /** ++ * 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/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 0000000000..a24330d2d4 --- /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 0000000000..90aac0f52f --- /dev/null +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/entity/Villager.java.patch @@ -0,0 +1,17 @@ +--- a/src/main/java/org/bukkit/entity/Villager.java ++++ b/src/main/java/org/bukkit/entity/Villager.java +@@ -367,4 +_,14 @@ + */ + public void clearReputations(); + // Paper end ++ ++ // 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 0000000000..1080c3d6d4 --- /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 +@@ -107,4 +_,20 @@ + */ + 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/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 0000000000..f3ac2ef4a4 --- /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 +@@ -110,4 +_,20 @@ + return RegistryAccess.registryAccess().getRegistry(RegistryKey.WOLF_VARIANT).getOrThrow(NamespacedKey.minecraft(key)); + } + } ++ ++ // 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/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 0000000000..0d86a78ae9 --- /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 +@@ -308,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 0000000000..24085dc5de --- /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 +@@ -216,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 a player gets bad omen after killing a patrol captain. + * 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 0000000000..ec2e9aabe5 --- /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 +@@ -164,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 0000000000..34a1144183 --- /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/0015-ItemStack-convenience-methods.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/ItemStack.java.patch similarity index 87% rename from patches/api/0015-ItemStack-convenience-methods.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/ItemStack.java.patch index 9beb1702d7..02c8dfc890 100644 --- a/patches/api/0015-ItemStack-convenience-methods.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/ItemStack.java.patch @@ -1,78 +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 e89edabd36a6755912694d8a8700da4ebe5c5829..ba2eff0f2ecffbea4b42d5c6e4485ee0087dc981 100644 ---- a/src/main/java/org/bukkit/Material.java -+++ b/src/main/java/org/bukkit/Material.java -@@ -5811,4 +5811,40 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla - return this.asItemType().getDefaultDataTypes(); - } - // Paper end - data component API -+ -+ // 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 8c9654cd19af8b28fa276a55c5060eb389e60c1c..875124b06d87cd4163f0ab1d4dd75f939622f8aa 100644 --- a/src/main/java/org/bukkit/inventory/ItemStack.java +++ b/src/main/java/org/bukkit/inventory/ItemStack.java -@@ -19,6 +19,13 @@ import org.bukkit.inventory.meta.ItemMeta; +@@ -21,6 +_,13 @@ import org.bukkit.material.MaterialData; 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; -+// Purpur end ++// Purpur end - ItemStack convenience methods /** * Represents a stack of items. -@@ -1318,4 +1325,482 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat +@@ -1329,4 +_,482 @@ return this.craftDelegate.matchesWithoutData(item, excludeTypes, ignoreCount); } // Paper end - data component API + -+ // Purpur start ++ // Purpur start - ItemStack convenience methods + /** + * Gets the display name that is set. + *

@@ -548,5 +495,5 @@ index 8c9654cd19af8b28fa276a55c5060eb389e60c1c..875124b06d87cd4163f0ab1d4dd75f93 + public boolean damage(int amount, boolean ignoreUnbreaking) { + return this.craftDelegate.damage(amount, ignoreUnbreaking); + } -+ // Purpur end ++ // Purpur end - ItemStack convenience methods } diff --git a/patches/api/0018-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/RecipeChoice.java.patch similarity index 52% rename from patches/api/0018-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch rename to purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/RecipeChoice.java.patch index e358db9801..177eef71ba 100644 --- a/patches/api/0018-Add-predicate-to-recipe-s-ExactChoice-ingredient.patch +++ b/purpur-api/paper-patches/files/src/main/java/org/bukkit/inventory/RecipeChoice.java.patch @@ -1,34 +1,27 @@ -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 922bb69b5f218e489a6dd5e0f207743c1f1d3d35..9b3e292be334d21eb978373f434bf3811ec4af2b 100644 --- a/src/main/java/org/bukkit/inventory/RecipeChoice.java +++ b/src/main/java/org/bukkit/inventory/RecipeChoice.java -@@ -191,6 +191,7 @@ public interface RecipeChoice extends Predicate, Cloneable { +@@ -191,6 +_,7 @@ public static class ExactChoice implements RecipeChoice { private List choices; -+ private Predicate predicate; // Purpur ++ private Predicate predicate; // Purpur - Add predicate to recipe's ExactChoice ingredient public ExactChoice(@NotNull ItemStack stack) { this(Arrays.asList(stack)); -@@ -241,6 +242,7 @@ public interface RecipeChoice extends Predicate, Cloneable { +@@ -241,6 +_,7 @@ @Override public boolean test(@NotNull ItemStack t) { -+ if (predicate != null) return predicate.test(t); // Purpur ++ 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; -@@ -250,6 +252,17 @@ public interface RecipeChoice extends Predicate, Cloneable { +@@ -249,6 +_,17 @@ + return false; } - -+ // Purpur start ++ ++ // Purpur start - Add predicate to recipe's ExactChoice ingredient + @org.jetbrains.annotations.Nullable + public Predicate getPredicate() { + return predicate; @@ -37,8 +30,7 @@ index 922bb69b5f218e489a6dd5e0f207743c1f1d3d35..9b3e292be334d21eb978373f434bf381 + public void setPredicate(@org.jetbrains.annotations.Nullable Predicate predicate) { + this.predicate = predicate; + } -+ // Purpur end -+ ++ // Purpur end - Add predicate to recipe's ExactChoice ingredient + @Override public int hashCode() { - int hash = 7; 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 0000000000..3a63aa786b --- /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 0000000000..80ffd257a5 --- /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 0000000000..e83156fc37 --- /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 0000000000..d4cb6a9fd1 --- /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 +@@ -55,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 0000000000..f8702315d6 --- /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 +@@ -68,6 +_,7 @@ + @Override + public void transferStarted(@NotNull TransferEvent event) throws TransferCancelledException + { ++ if (!JavaPluginLoader.SuppressLibraryLoaderLogger) // Purpur - Add log suppression for LibraryLoader + logger.log( Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName() ); + } + } ); +@@ -94,6 +_,7 @@ + { + 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 +@@ -144,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 0000000000..1b18651ec9 --- /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 0000000000..4cf992931f --- /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 0000000000..6d4395fbf0 --- /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 0000000000..29540d5553 --- /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 0000000000..55feef2321 --- /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 0000000000..e9637b8201 --- /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 0000000000..795c558b48 --- /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 0000000000..1d4dbf60a1 --- /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 0000000000..4b4d32c582 --- /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 0000000000..7f631a41ab --- /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 0000000000..e260145d6d --- /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 0000000000..8b2b351d62 --- /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/EntityTeleportHinderedEvent.java b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/EntityTeleportHinderedEvent.java new file mode 100644 index 0000000000..daf3bbf83e --- /dev/null +++ b/purpur-api/src/main/java/org/purpurmc/purpur/event/entity/EntityTeleportHinderedEvent.java @@ -0,0 +1,114 @@ +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.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Fired when an entity is hindered from teleporting. + */ +@NullMarked +public class EntityTeleportHinderedEvent extends EntityEvent { + private static final HandlerList handlers = new HandlerList(); + + private final Reason reason; + + private final @Nullable TeleportCause teleportCause; + + private boolean retry = false; + + @ApiStatus.Internal + public EntityTeleportHinderedEvent(Entity what, Reason reason, @Nullable TeleportCause teleportCause) { + super(what); + this.reason = reason; + this.teleportCause = teleportCause; + } + + /** + * @return why the teleport was hindered. + */ + 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 + public HandlerList getHandlers() { + return handlers; + } + + 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/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 0000000000..f0a7fe694d --- /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 0000000000..e34c37579d --- /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 0000000000..23ea41ff5d --- /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 0000000000..d56fb06645 --- /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 0000000000..c31a656daa --- /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 0000000000..02de629f06 --- /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 0000000000..b2199854b5 --- /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 0000000000..4293c4a57c --- /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 0000000000..d6db2d3555 --- /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 0000000000..31cce9f4e3 --- /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 0000000000..cbdad4cf09 --- /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 0000000000..50647252ed --- /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 0000000000..04c855e1a4 --- /dev/null +++ b/purpur-server/build.gradle.kts.patch @@ -0,0 +1,85 @@ +--- a/paper-server/build.gradle.kts ++++ b/paper-server/build.gradle.kts +@@ -21,6 +_,20 @@ + // macheOldPath = file("F:\\Projects\\PaperTooling\\mache\\versions\\1.21.4\\src\\main\\java") + // gitFilePatches = true + ++ 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 ++ ++ paper { ++ paperServerDir = upstreamsDirectory().map { it.dir("paper/paper-server") } ++ } ++ + spigot { + buildDataRef = "3edaf46ec1eed4115ce1b18d2846cded42577e42" + packageVersion = "v1_21_R3" // also needs to be updated in MappingEnvironment +@@ -101,7 +_,20 @@ + } + } + +-val log4jPlugins = sourceSets.create("log4jPlugins") ++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") } ++} + configurations.named(log4jPlugins.compileClasspathConfigurationName) { + extendsFrom(configurations.compileClasspath.get()) + } +@@ -119,7 +_,7 @@ + } + + dependencies { +- implementation(project(":paper-api")) ++ implementation(project(":purpur-api")) + implementation("ca.spottedleaf:concurrentutil:0.0.3") + 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 +@@ -149,6 +_,10 @@ + runtimeOnly("com.mysql:mysql-connector-j:9.1.0") + runtimeOnly("com.lmax:disruptor:3.4.4") + ++ 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 ++ + runtimeOnly("org.apache.maven:maven-resolver-provider:3.9.6") + runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18") + runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18") +@@ -188,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, 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 0000000000..78c7aab628 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0001-Ridables.patch @@ -0,0 +1,5108 @@ +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 29d402620d2e1cbed94f941f933ae8eb5d786e7f..ec0998369158286fccb38c8e10c3cfa2a653a8aa 100644 +--- a/net/minecraft/gametest/framework/GameTestHelper.java ++++ b/net/minecraft/gametest/framework/GameTestHelper.java +@@ -281,6 +281,8 @@ public class GameTestHelper { + + public void setAfk(final boolean afk) {} // Purpur - AFK API + ++ public void resetLastActionTime() {} // Purpur - Ridables ++ + @Override + public boolean isLocalPlayer() { + return true; +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index 121b57c7f5345f5d8884eaa1d36dac79fb7d42ef..9afbfe9bf493e09ca1963e8956ab7573964479b4 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -1745,6 +1745,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().location()); + /* 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 345e5c7aff07544e24f2e91d71585b06392d9927..fd84a5aac26bbf2de2c2fa8bb5bc76bcbd09c3e4 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -217,6 +217,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 + + public LevelChunk getChunkIfLoaded(int x, int z) { + return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately +diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java +index bb8f89d9a5f0bb69baea6ba241cc391b43f2314e..e4a27ef7c201b2f8c9fbb12a7dbcf704b9a92f2f 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -848,6 +848,15 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc + this.trackEnteredOrExitedLavaOnVehicle(); + this.updatePlayerAttributes(); + this.advancements.flushDirty(this); ++ ++ // 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 d0dc5d6f25b7514bc3c65bbef5b40703f6d0eed7..f524f1de2093e0a3bd2f3cf8806232b2211d0583 100644 +--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -2828,6 +2828,8 @@ public class ServerGamePacketListenerImpl + + ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event); + ++ player.processClick(hand); // Purpur - Ridables ++ + // Entity in bucket - SPIGOT-4048 and SPIGOT-6859a + if ((target instanceof Bucketable && target instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) { + 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 474e5b8163e82a76aea8c4402d6acfcd366acaef..e4ee886f33ee63ae740cd89067c8ec81c12f2a62 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -3155,6 +3155,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + 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 ++ + this.gameEvent(GameEvent.ENTITY_MOUNT, passenger); + } + } +@@ -3196,6 +3203,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + 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 { +@@ -5119,4 +5134,44 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + 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, 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 - Ridables + } +diff --git a/net/minecraft/world/entity/GlowSquid.java b/net/minecraft/world/entity/GlowSquid.java +index 95d78dcdb6777df73898694367ee17b1cb76d7a2..d0313fd5368baa53ec511c8c07fc78a1f1ecec4e 100644 +--- a/net/minecraft/world/entity/GlowSquid.java ++++ b/net/minecraft/world/entity/GlowSquid.java +@@ -32,6 +32,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/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index f41d5ffe83e3cfb4c30d150f8b66f8f2568ae20c..8c2bdb1775f7c4110c5f967b1052eba6a8fcbbfa 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -250,9 +250,9 @@ public abstract class LivingEntity extends Entity implements Attackable { + protected float rotOffs; + 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; +@@ -310,7 +310,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + + protected LivingEntity(EntityType entityType, Level level) { + super(entityType, level); +- this.attributes = new AttributeMap(DefaultAttributes.getSupplier(entityType)); ++ this.attributes = new AttributeMap(DefaultAttributes.getSupplier(entityType), this); // Purpur - Ridables + this.craftAttributes = new 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()); +@@ -377,6 +377,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + .add(Attributes.MOVEMENT_EFFICIENCY) + .add(Attributes.ATTACK_KNOCKBACK); + } ++ public boolean shouldSendAttribute(Attribute attribute) { return true; } // Purpur - Ridables + + @Override + protected void checkFallDamage(double y, boolean onGround, BlockState state, BlockPos pos) { +@@ -3537,8 +3538,10 @@ public abstract class LivingEntity extends Entity implements Attackable { + 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()); +@@ -3548,6 +3551,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 - 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()) { ++ 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 - Ridables + } + // Paper end - Add EntityMoveEvent + if (this.level() instanceof ServerLevel serverLevel && this.isSensitiveToWater() && this.isInWaterRainOrBubble()) { +diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java +index 26f7cd5ddacf5f908702adbf55b56dcc6fcbe162..c431f28c3f4f6cec946048f5752c364429af5ba1 100644 +--- a/net/minecraft/world/entity/Mob.java ++++ b/net/minecraft/world/entity/Mob.java +@@ -151,8 +151,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + super(entityType, 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); +@@ -1405,7 +1405,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 + } + + public boolean isWithinRestriction() { +@@ -1723,4 +1723,58 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + public float[] getArmorDropChances() { + return this.armorDropChances; + } ++ ++ // 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 4c808c7ef336de74048f40bd1cc8b14131a9325d..a25d74592e89e3d6339479c6dc2b6f45d1932cfc 100644 +--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java ++++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java +@@ -23,14 +23,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); + } + } +@@ -44,7 +51,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 + } + + @Nullable +diff --git a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java +index 33527a1825119f3667fb3c7ccec318f2c7328ec9..61ed4d687120fcbb7b91863e400f3657ebcde687 100644 +--- a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java ++++ b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java +@@ -131,7 +131,7 @@ public class DefaultAttributes { + .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.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 0f9bf0cb0655a6ed449a86e99b17f89b4e3264df..1860b4ab2314f5da017313977c6423e735a4f96b 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 4d715a29f1ad31e87977562bd0e2aeddb54ee082..e7ea944e77175ee4051b8e7361c502d0cc2115d5 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 entityType, Level level) { + super(entityType, 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; +@@ -98,7 +145,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() { +@@ -129,6 +176,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/AbstractFish.java b/net/minecraft/world/entity/animal/AbstractFish.java +index c0997c8c0f8ee4474d3acdd5938b1879c4e589a2..28ae152125ed83d8917674b6068f227f87890f30 100644 +--- a/net/minecraft/world/entity/animal/AbstractFish.java ++++ b/net/minecraft/world/entity/animal/AbstractFish.java +@@ -87,6 +87,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::test)); + this.goalSelector.addGoal(4, new AbstractFish.FishSwimGoal(this)); +@@ -100,7 +101,7 @@ public abstract class AbstractFish extends WaterAnimal implements Bucketable { + @Override + public void travel(Vec3 travelVector) { + if (this.isControlledByLocalInstance() && this.isInWater()) { +- 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/Bee.java b/net/minecraft/world/entity/animal/Bee.java +index 3793570827eb6ca21c6b990d76c679c00ad100f4..af0cf64b4c74d290dec8032f8a6127867e301130 100644 +--- a/net/minecraft/world/entity/animal/Bee.java ++++ b/net/minecraft/world/entity/animal/Bee.java +@@ -145,6 +145,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + + public Bee(EntityType entityType, Level level) { + super(entityType, 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 entity, final int maxPitchChange, final boolean noGravity) { +@@ -153,11 +154,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 +@@ -169,6 +183,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); +@@ -183,6 +231,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)); +@@ -200,6 +249,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(new Class[0])); + this.targetSelector.addGoal(2, new Bee.BeeBecomeAngryTargetGoal(this)); + this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true)); +@@ -1084,15 +1134,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/Cat.java b/net/minecraft/world/entity/animal/Cat.java +index 618f184ce9fed4d9b01f2df4d9a4476d20a55546..f066b0acfa0e954f6d71e62962c76afa1f05a4a5 100644 +--- a/net/minecraft/world/entity/animal/Cat.java ++++ b/net/minecraft/world/entity/animal/Cat.java +@@ -93,10 +93,36 @@ public class Cat extends TamableAnimal implements VariantHolder itemStack.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)); +@@ -109,6 +135,7 @@ public class Cat extends TamableAnimal implements VariantHolder(this, Rabbit.class, false, null)); + this.targetSelector.addGoal(1, new NonTameRandomTargetGoal<>(this, Turtle.class, false, Turtle.BABY_ON_LAND_SELECTOR)); + } +@@ -360,6 +387,7 @@ public class Cat extends TamableAnimal implements VariantHolder itemStack.is(ItemTags.CHICKEN_FOOD), false)); +diff --git a/net/minecraft/world/entity/animal/Cod.java b/net/minecraft/world/entity/animal/Cod.java +index 708bcc39e7242292d5d5bfcaf599e3738628df9b..6a19086e272363701260801f3c6db9b5c91b8ef5 100644 +--- a/net/minecraft/world/entity/animal/Cod.java ++++ b/net/minecraft/world/entity/animal/Cod.java +@@ -13,6 +13,18 @@ public class Cod extends AbstractSchoolingFish { + super(entityType, 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/Cow.java b/net/minecraft/world/entity/animal/Cow.java +index 8c1f74c6be53cbf48bd6b5641511359578801c08..656babc0c8810a85eb9f78ced1f3ad9551fdc286 100644 +--- a/net/minecraft/world/entity/animal/Cow.java ++++ b/net/minecraft/world/entity/animal/Cow.java +@@ -38,9 +38,27 @@ public class Cow extends Animal { + super(entityType, 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 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 +@@ -86,13 +104,14 @@ public class Cow 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((ServerLevel) 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/Dolphin.java b/net/minecraft/world/entity/animal/Dolphin.java +index 8be0dd148d88dfdfb9efab91124c829e60b5dea5..35bce598bb5857356823594d2a001006ce19f835 100644 +--- a/net/minecraft/world/entity/animal/Dolphin.java ++++ b/net/minecraft/world/entity/animal/Dolphin.java +@@ -72,14 +72,82 @@ public class Dolphin extends AgeableWaterCreature { + public static final Predicate ALLOWED_ITEMS = itemEntity -> !itemEntity.hasPickUpDelay() && itemEntity.isAlive() && itemEntity.isInWater(); + public static final float BABY_SCALE = 0.65F; + private boolean isNaturallyAggressiveToPlayers; // Purpur - Dolphins naturally aggressive to players chance ++ private int spitCooldown; // Purpur - Ridables + + public Dolphin(EntityType entityType, Level level) { + super(entityType, 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 ++ + @Nullable + @Override + public SpawnGroupData finalizeSpawn( +@@ -172,6 +240,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)); +@@ -182,6 +251,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 + } +@@ -227,7 +297,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 +@@ -256,6 +326,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/Fox.java b/net/minecraft/world/entity/animal/Fox.java +index ddc252c76cedec0a0e9e268d8a874015a5ad52fe..8b0a813f9dd001c6dd108ba7aac04d134a20fbc1 100644 +--- a/net/minecraft/world/entity/animal/Fox.java ++++ b/net/minecraft/world/entity/animal/Fox.java +@@ -129,6 +129,44 @@ public class Fox extends Animal implements VariantHolder { + 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); +@@ -148,6 +186,7 @@ public class Fox extends Animal implements VariantHolder { + 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)); +@@ -175,6 +214,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 - Ridables + this.targetSelector + .addGoal( + 3, +@@ -1095,15 +1135,15 @@ public class Fox extends Animal implements VariantHolder { + } + } + +- 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 + } + } + +@@ -1139,15 +1179,15 @@ public class Fox extends Animal implements VariantHolder { + } + } + +- 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/IronGolem.java b/net/minecraft/world/entity/animal/IronGolem.java +index 46921562c9c5caf7e04ee180325a638273d6bad2..223c4796f659a24062a719045e484a22d31ab2f0 100644 +--- a/net/minecraft/world/entity/animal/IronGolem.java ++++ b/net/minecraft/world/entity/animal/IronGolem.java +@@ -73,9 +73,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)); +@@ -83,6 +102,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/MushroomCow.java b/net/minecraft/world/entity/animal/MushroomCow.java +index a8aeb79b1c1413d74a5d18a57bd4ba4beca6039c..1292146341022483f78a9128ef9d7a88089274a0 100644 +--- a/net/minecraft/world/entity/animal/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/MushroomCow.java +@@ -55,6 +55,23 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder 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/Ocelot.java b/net/minecraft/world/entity/animal/Ocelot.java +index e193696b2e3eb1c1c689c05592ab4318a98772ad..d26a8658c8c56c3b0df4e5908de1ac23ac2dd351 100644 +--- a/net/minecraft/world/entity/animal/Ocelot.java ++++ b/net/minecraft/world/entity/animal/Ocelot.java +@@ -62,6 +62,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); + } +@@ -93,12 +110,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/Panda.java b/net/minecraft/world/entity/animal/Panda.java +index 283ddf7d13a17c0a6df5a52b7fd26ed7b7a4826b..19aa39af6685a03eb584820853239a3f4fa1a515 100644 +--- a/net/minecraft/world/entity/animal/Panda.java ++++ b/net/minecraft/world/entity/animal/Panda.java +@@ -105,6 +105,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(); +@@ -258,6 +284,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)); +@@ -273,6 +300,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(new Class[0])); + } + +@@ -616,7 +644,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; +@@ -652,7 +680,7 @@ public class Panda extends Animal { + + return InteractionResult.SUCCESS_SERVER; + } else { +- return InteractionResult.PASS; ++ return tryRide(player, hand); // Purpur - Ridables + } + } + +@@ -964,7 +992,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) { +@@ -973,9 +1001,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.java b/net/minecraft/world/entity/animal/Parrot.java +index 1d840fe1c718ea4431c471e3cbbdee074ed53179..445614d09d2364daee5245c217baeb31e186c168 100644 +--- a/net/minecraft/world/entity/animal/Parrot.java ++++ b/net/minecraft/world/entity/animal/Parrot.java +@@ -124,12 +124,68 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder entityType, Level level) { + super(entityType, 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 ++ + @Nullable + @Override + public SpawnGroupData finalizeSpawn( +@@ -150,9 +206,11 @@ public class Parrot extends ShoulderRidingEntity implements VariantHolder itemStack.is(Items.CARROT_ON_A_STICK), false)); +diff --git a/net/minecraft/world/entity/animal/PolarBear.java b/net/minecraft/world/entity/animal/PolarBear.java +index fbd35f074a3045d483aabd9bc7e1c9c4f10a3167..711ed0d753494a92a003fc683146f289505ed7f6 100644 +--- a/net/minecraft/world/entity/animal/PolarBear.java ++++ b/net/minecraft/world/entity/animal/PolarBear.java +@@ -59,6 +59,7 @@ public class PolarBear extends Animal implements NeutralMob { + private int remainingPersistentAngerTime; + @Nullable + private UUID persistentAngerTarget; ++ private int standTimer = 0; // Purpur - Ridables + + public PolarBear(EntityType entityType, Level level) { + super(entityType, level); +@@ -87,6 +88,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 ++ + @Nullable + @Override + public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob otherParent) { +@@ -102,6 +131,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 +@@ -114,6 +144,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/Pufferfish.java b/net/minecraft/world/entity/animal/Pufferfish.java +index d94a7cfcd0f7a15ce97d3b12daa8b2c71acf997a..f7e9abf778186ad1c78dbe411980a83c5e68792e 100644 +--- a/net/minecraft/world/entity/animal/Pufferfish.java ++++ b/net/minecraft/world/entity/animal/Pufferfish.java +@@ -45,6 +45,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/Rabbit.java b/net/minecraft/world/entity/animal/Rabbit.java +index b2cbe9f7a771dbfc381effa0821d44421c98b33e..8cac46951938c80fae3499e8b53709c25d86e9bd 100644 +--- a/net/minecraft/world/entity/animal/Rabbit.java ++++ b/net/minecraft/world/entity/animal/Rabbit.java +@@ -83,6 +83,7 @@ public class Rabbit extends Animal implements VariantHolder { + private boolean wasOnGround; + private int jumpDelayTicks; + public int moreCarrotTicks; ++ private boolean actualJump; // Purpur - Ridables + + public Rabbit(EntityType entityType, Level level) { + super(entityType, level); +@@ -91,9 +92,55 @@ public class Rabbit extends Animal implements VariantHolder { + //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)); +@@ -108,6 +155,14 @@ public class Rabbit extends Animal implements VariantHolder { + + @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; +@@ -175,6 +230,12 @@ public class Rabbit extends Animal implements VariantHolder { + + @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--; + } +@@ -483,7 +544,7 @@ public class Rabbit extends Animal implements VariantHolder { + } + } + +- static class RabbitMoveControl extends MoveControl { ++ static class RabbitMoveControl extends org.purpurmc.purpur.controller.MoveControllerWASD { // Purpur - Ridables + private final Rabbit rabbit; + private double nextJumpSpeed; + +@@ -493,14 +554,14 @@ public class Rabbit extends Animal implements VariantHolder { + } + + @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/Salmon.java b/net/minecraft/world/entity/animal/Salmon.java +index 41366f7b9af176a33b20ea26dd53d50994d2c600..ebbd6d39c3f5d6c66445c2c743785ed369408389 100644 +--- a/net/minecraft/world/entity/animal/Salmon.java ++++ b/net/minecraft/world/entity/animal/Salmon.java +@@ -35,6 +35,18 @@ public class Salmon extends AbstractSchoolingFish implements VariantHolder stack.is(ItemTags.SHEEP_FOOD), false)); +diff --git a/net/minecraft/world/entity/animal/SnowGolem.java b/net/minecraft/world/entity/animal/SnowGolem.java +index 29427515b648b84248f486c156c5cd7a0995ba14..52de92b118b613217b8f92ff672c01ddf798a1fc 100644 +--- a/net/minecraft/world/entity/animal/SnowGolem.java ++++ b/net/minecraft/world/entity/animal/SnowGolem.java +@@ -61,12 +61,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)); + } + +@@ -113,6 +132,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++) { +@@ -155,7 +175,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 +@@ -176,7 +196,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/Squid.java b/net/minecraft/world/entity/animal/Squid.java +index c776d40896a6514ab9c66df206c93469ec682b23..e3f43e8c6ddbae289a82157cab4beb18f682dd75 100644 +--- a/net/minecraft/world/entity/animal/Squid.java ++++ b/net/minecraft/world/entity/animal/Squid.java +@@ -69,9 +69,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()); + } + +@@ -327,6 +350,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/TropicalFish.java b/net/minecraft/world/entity/animal/TropicalFish.java +index fa5f7f7d54083f9ea2095dd44362069d00e0b9a5..1e31a39b276e1c5ae767da7af0b536007c87189e 100644 +--- a/net/minecraft/world/entity/animal/TropicalFish.java ++++ b/net/minecraft/world/entity/animal/TropicalFish.java +@@ -67,6 +67,18 @@ public class TropicalFish extends AbstractSchoolingFish implements VariantHolder + super(entityType, 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/Turtle.java b/net/minecraft/world/entity/animal/Turtle.java +index 354ec2b987882d8f40ef4ac5257183d2fda73bb8..98cb91574c8d2bdb6d180256f657ecc67987a6fe 100644 +--- a/net/minecraft/world/entity/animal/Turtle.java ++++ b/net/minecraft/world/entity/animal/Turtle.java +@@ -84,6 +84,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.entityData.set(HOME_POS, homePos); + } +@@ -188,6 +205,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)); +@@ -539,12 +557,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() { +@@ -563,7 +583,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(); +@@ -577,7 +597,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.java b/net/minecraft/world/entity/animal/Wolf.java +index 6cc3893742b443ec84942252910cf444cdbf0c96..90609ff3060322110ece27630de0abae1a6370a8 100644 +--- a/net/minecraft/world/entity/animal/Wolf.java ++++ b/net/minecraft/world/entity/animal/Wolf.java +@@ -180,9 +180,32 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder(this, Llama.class, 24.0F, 1.5, 1.5)); +@@ -195,6 +218,7 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder entityType, Level level) { + super(entityType, 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(); +@@ -138,6 +151,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); +@@ -247,6 +282,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()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // 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 dfdbcb31458095a71c187efc2774ecc4945dd11b..87a190d8646d8bbed8c182f9f0f7d8c398e63d26 100644 +--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java ++++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java +@@ -80,6 +80,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 9faa929734035c167e54569ce34d841291856589..2054e4624da0c9b04ea69b9bf39443c4574d48be 100644 +--- a/net/minecraft/world/entity/animal/axolotl/Axolotl.java ++++ b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +@@ -115,6 +115,23 @@ public class Axolotl extends Animal implements VariantHolder, B + 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; +@@ -304,6 +321,7 @@ public class Axolotl extends Animal implements VariantHolder, B + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("axolotlBrain"); ++ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + profilerFiller.push("axolotlActivityUpdate"); +@@ -555,23 +573,31 @@ public class Axolotl extends Animal implements VariantHolder, B + } + + @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/camel/Camel.java b/net/minecraft/world/entity/animal/camel/Camel.java +index 3ac169f83c5619b5c00c866354a2e066a0a738cc..11311d2ec37d825e73e2218e60e2606dd3a25a1d 100644 +--- a/net/minecraft/world/entity/animal/camel/Camel.java ++++ b/net/minecraft/world/entity/animal/camel/Camel.java +@@ -83,6 +83,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 + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); +diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java +index 12c655b60087a2f6122ffa508b3224159d8777b0..9a400c8bf2b54aa5fbcbe65b61670cac5fbebf05 100644 +--- a/net/minecraft/world/entity/animal/frog/Frog.java ++++ b/net/minecraft/world/entity/animal/frog/Frog.java +@@ -106,6 +106,8 @@ public class Frog extends Animal implements 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 - Ridables ++ private org.purpurmc.purpur.controller.WaterMoveControllerWASD purpurWaterController; // Purpur - Ridables + + public Frog(EntityType entityType, Level level) { + super(entityType, level); +@@ -113,7 +115,55 @@ public class Frog extends Animal implements VariantHolder> { + 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() { +@@ -188,6 +238,7 @@ public class Frog extends Animal implements VariantHolder> { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("frogBrain"); ++ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // Purpur - only use brain if no rider + this.getBrain().tick(level, this); + profilerFiller.pop(); + profilerFiller.push("frogActivityUpdate"); +@@ -380,7 +431,7 @@ public class Frog extends Animal implements VariantHolder> { + 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 97adf8142cdd322c4873c420ed760e9dee34da23..e888e606b4b14fa6485de7426bc146b6005962af 100644 +--- a/net/minecraft/world/entity/animal/frog/Tadpole.java ++++ b/net/minecraft/world/entity/animal/frog/Tadpole.java +@@ -63,13 +63,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 entityType, Level level) { + super(entityType, 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); +@@ -99,6 +136,7 @@ public class Tadpole extends AbstractFish { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("tadpoleBrain"); ++ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // 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 30fd6bfad846c7a268ab87254974bad2ee0e0422..c4c78885373347ee052be9575b9f3b8dd9f2b781 100644 +--- a/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/net/minecraft/world/entity/animal/goat/Goat.java +@@ -111,6 +111,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); +@@ -188,6 +205,7 @@ public class Goat extends Animal { + protected void customServerAiStep(ServerLevel level) { + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("goatBrain"); ++ //if ((getRider() == null || !this.isControllable()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // 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/horse/AbstractHorse.java b/net/minecraft/world/entity/animal/horse/AbstractHorse.java +index d52a8315f1e6876c26c732f4c4caa47bc6bebf6e..828406060e50ff62586929371aafb46ef7d81f92 100644 +--- a/net/minecraft/world/entity/animal/horse/AbstractHorse.java ++++ b/net/minecraft/world/entity/animal/horse/AbstractHorse.java +@@ -206,11 +206,21 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, + + protected AbstractHorse(EntityType entityType, Level level) { + super(entityType, 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 PanicGoal(this, 1.2)); + this.goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2)); + this.goalSelector.addGoal(2, new BreedGoal(this, 1.0, AbstractHorse.class)); +@@ -221,6 +231,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 - Ridables + + this.addBehaviourGoals(); + } +diff --git a/net/minecraft/world/entity/animal/horse/Donkey.java b/net/minecraft/world/entity/animal/horse/Donkey.java +index 9b97f3d3675f5051b18a68ff7fa056d859a283e9..ee3fa710e95f2e84f7f9bdce1159d1136815172d 100644 +--- a/net/minecraft/world/entity/animal/horse/Donkey.java ++++ b/net/minecraft/world/entity/animal/horse/Donkey.java +@@ -16,6 +16,13 @@ public class Donkey extends AbstractChestedHorse { + super(entityType, level); + } + ++ // Purpur start - Ridables ++ @Override ++ public boolean dismountsUnderwater() { ++ return level().purpurConfig.useDismountsUnderwaterTag ? super.dismountsUnderwater() : !level().purpurConfig.donkeyRidableInWater; ++ } ++ // Purpur end - Ridables ++ + @Override + protected SoundEvent getAmbientSound() { + return SoundEvents.DONKEY_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/horse/Horse.java b/net/minecraft/world/entity/animal/horse/Horse.java +index c6d0700f29d6c8123e96efe225faf2d99202ac81..361bf346153912bcbfcf962d7f716dfe12ae2a7b 100644 +--- a/net/minecraft/world/entity/animal/horse/Horse.java ++++ b/net/minecraft/world/entity/animal/horse/Horse.java +@@ -43,6 +43,13 @@ public class Horse extends AbstractHorse implements VariantHolder { + super(entityType, level); + } + ++ // 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/horse/Llama.java b/net/minecraft/world/entity/animal/horse/Llama.java +index 7d4aad3c45d710488aba540ee5a535098ddd27ee..164a429d432badcb315e8ece406e29e576a11265 100644 +--- a/net/minecraft/world/entity/animal/horse/Llama.java ++++ b/net/minecraft/world/entity/animal/horse/Llama.java +@@ -78,7 +78,51 @@ public class Llama extends AbstractChestedHorse implements VariantHolder entityType, 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); + } +@@ -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) { +@@ -298,7 +374,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( +@@ -348,9 +424,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/wither/WitherBoss.java b/net/minecraft/world/entity/boss/wither/WitherBoss.java +index 95cf215e8804cc2d7b681723dfebd1dcb8cbaeee..5d97ae09292fb3209e7362df778e88dc508815a3 100644 +--- a/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -69,6 +69,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 +@@ -78,9 +79,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); + @Nullable private java.util.UUID summoner; // Purpur - Summoner API ++ private org.purpurmc.purpur.controller.FlyingWithSpacebarMoveControllerWASD purpurController; // Purpur - Ridables + + public WitherBoss(EntityType entityType, Level level) { + super(entityType, 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; +@@ -97,6 +112,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); +@@ -107,11 +221,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)); + } +@@ -271,6 +387,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); +@@ -577,11 +702,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/AbstractSkeleton.java b/net/minecraft/world/entity/monster/AbstractSkeleton.java +index 1e97cb34aa22ad3150b598232dd339213b236f5c..e186aee80b052b7fc4bfe02763010bfb2d55ea35 100644 +--- a/net/minecraft/world/entity/monster/AbstractSkeleton.java ++++ b/net/minecraft/world/entity/monster/AbstractSkeleton.java +@@ -73,12 +73,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/Blaze.java b/net/minecraft/world/entity/monster/Blaze.java +index 419c729502ee708bba9e750f1b951450eca82695..201b08a75c42d90e657c3d56fc6691839e87199c 100644 +--- a/net/minecraft/world/entity/monster/Blaze.java ++++ b/net/minecraft/world/entity/monster/Blaze.java +@@ -33,6 +33,7 @@ public class Blaze extends Monster { + + public Blaze(EntityType entityType, Level level) { + super(entityType, 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); +@@ -40,19 +41,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 +@@ -117,6 +154,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/Bogged.java b/net/minecraft/world/entity/monster/Bogged.java +index f01670f7106a39957c9b37839fcca0d9f29208f0..2b603c1242aac307f28bae5a85bcaad309f929f5 100644 +--- a/net/minecraft/world/entity/monster/Bogged.java ++++ b/net/minecraft/world/entity/monster/Bogged.java +@@ -41,6 +41,23 @@ public class Bogged extends AbstractSkeleton implements Shearable { + super(entityType, 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/CaveSpider.java b/net/minecraft/world/entity/monster/CaveSpider.java +index 2e32567fca7a2a4cd87bc078a6eeb30e3ffabfce..4873a3d8dd9c160ecdbda594ee546c35ec03a1e7 100644 +--- a/net/minecraft/world/entity/monster/CaveSpider.java ++++ b/net/minecraft/world/entity/monster/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 source) { + if (super.doHurtTarget(level, source)) { +diff --git a/net/minecraft/world/entity/monster/Creeper.java b/net/minecraft/world/entity/monster/Creeper.java +index 9aff3c911a56885ab3c34bb34bb05e7b1c00d3bd..cdcee233ed0c272e4a68a2a709fe92b21bc6c22c 100644 +--- a/net/minecraft/world/entity/monster/Creeper.java ++++ b/net/minecraft/world/entity/monster/Creeper.java +@@ -51,21 +51,98 @@ public class Creeper extends Monster { + private int droppedSkulls; + public 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 entityType, Level level) { + super(entityType, 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)); + } +@@ -312,6 +389,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/Drowned.java b/net/minecraft/world/entity/monster/Drowned.java +index 6c1e816356243686f7d0bfa031badc75b54b215d..c1da9ebee7e870a9143e6224be9e9f4e62232459 100644 +--- a/net/minecraft/world/entity/monster/Drowned.java ++++ b/net/minecraft/world/entity/monster/Drowned.java +@@ -75,6 +75,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 void addBehaviourGoals() { + this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0)); +@@ -408,7 +425,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) { +@@ -417,7 +434,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) { +@@ -437,7 +454,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)); +@@ -446,7 +463,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/ElderGuardian.java b/net/minecraft/world/entity/monster/ElderGuardian.java +index 4585b7c867685f8419c4d2b5b90af5946d337f90..c6eeaf7b460408acfdf89d988b47b08eab7df4c5 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 8709588083fd5ca6a31d9a8d4096475d117915a1..cf511c78638e0d7aa652d1c880b3cd8172d5b3cf 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -90,9 +90,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)); +@@ -100,6 +118,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 +@@ -272,7 +291,7 @@ public class EnderMan extends Monster implements NeutralMob { + + @Override + protected void customServerAiStep(ServerLevel level) { +- if (level.isDay() && this.tickCount >= this.targetChangeTime + 600) { ++ if ((getRider() == null || !this.isControllable()) && level.isDay() && this.tickCount >= this.targetChangeTime + 600) { // Purpur - Ridables - no random teleporting + float lightLevelDependentMagicValue = this.getLightLevelDependentMagicValue(); + if (lightLevelDependentMagicValue > 0.5F + && level.canSeeSky(this.blockPosition()) +@@ -385,6 +404,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 { + boolean flag = damageSource.getDirectEntity() instanceof ThrownPotion; +diff --git a/net/minecraft/world/entity/monster/Endermite.java b/net/minecraft/world/entity/monster/Endermite.java +index 4e00daa6ece386f70502c074084b7b1b64caac2f..f4ab2e984dd87d2372aa10d2cbfd03a3f6fb1249 100644 +--- a/net/minecraft/world/entity/monster/Endermite.java ++++ b/net/minecraft/world/entity/monster/Endermite.java +@@ -45,14 +45,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/Evoker.java b/net/minecraft/world/entity/monster/Evoker.java +index b70ea1af39cada6bb17001c6b65502510e34c4b2..2eaeb0c0c0cb917506443ed1380b81f317961d53 100644 +--- a/net/minecraft/world/entity/monster/Evoker.java ++++ b/net/minecraft/world/entity/monster/Evoker.java +@@ -50,10 +50,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)); +@@ -63,6 +81,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/Ghast.java b/net/minecraft/world/entity/monster/Ghast.java +index b97bbfbbc8c1a4f38b4b858ef4915b637cc8a627..00c05fb5736c90c94f6fe51793acf8b65b1d0505 100644 +--- a/net/minecraft/world/entity/monster/Ghast.java ++++ b/net/minecraft/world/entity/monster/Ghast.java +@@ -42,11 +42,47 @@ public class Ghast extends FlyingMob implements Enemy { + this.moveControl = new Ghast.GhastMoveControl(this); + } + ++ // 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; ++ } ++ ++ @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(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)); + } +@@ -101,7 +137,7 @@ public class Ghast extends FlyingMob implements Enemy { + } + + public static AttributeSupplier.Builder createAttributes() { +- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0).add(Attributes.FOLLOW_RANGE, 100.0); ++ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0).add(Attributes.FOLLOW_RANGE, 100.0).add(Attributes.FLYING_SPEED, 0.6D); // Purpur - Ridables + } + + @Override +@@ -191,7 +227,7 @@ public class Ghast extends FlyingMob implements Enemy { + } + } + +- static class GhastMoveControl extends MoveControl { ++ static class GhastMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - Ridables + private final Ghast ghast; + private int floatDuration; + +@@ -201,7 +237,7 @@ public class Ghast extends FlyingMob implements Enemy { + } + + @Override +- public void tick() { ++ public void vanillaTick() { // Purpur - Ridables + if (this.operation == MoveControl.Operation.MOVE_TO) { + if (this.floatDuration-- <= 0) { + this.floatDuration = this.floatDuration + this.ghast.getRandom().nextInt(5) + 2; +diff --git a/net/minecraft/world/entity/monster/Giant.java b/net/minecraft/world/entity/monster/Giant.java +index 969eb604851d1cce50f0f99ed479189061d5de0c..135f83484ac31db7dcc225ba6f94e2e4ca27eea8 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(entityType, 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).add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.ATTACK_DAMAGE, 50.0); + } +diff --git a/net/minecraft/world/entity/monster/Guardian.java b/net/minecraft/world/entity/monster/Guardian.java +index c8e249b8f7ee8e9c075169ec988f5a0d459a3767..c20c744522459d938c772077e542ba433bc4c80e 100644 +--- a/net/minecraft/world/entity/monster/Guardian.java ++++ b/net/minecraft/world/entity/monster/Guardian.java +@@ -66,14 +66,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); +@@ -82,6 +103,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))); + } + +@@ -344,7 +366,7 @@ public class Guardian extends Monster { + @Override + public void travel(Vec3 travelVector) { + if (this.isControlledByLocalInstance() && this.isInWater()) { +- 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) { +@@ -452,7 +474,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) { +@@ -460,8 +482,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(); +@@ -471,7 +502,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/Husk.java b/net/minecraft/world/entity/monster/Husk.java +index 6155c544ad2722a49c5e41dd7d7b02fedc56474e..23936305045299352561e866b6a28aa515cd614a 100644 +--- a/net/minecraft/world/entity/monster/Husk.java ++++ b/net/minecraft/world/entity/monster/Husk.java +@@ -21,6 +21,23 @@ public class Husk extends Zombie { + super(entityType, 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 ++ + public static boolean checkHuskSpawnRules( + EntityType entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/Illusioner.java b/net/minecraft/world/entity/monster/Illusioner.java +index 40ca12e391b2adac6b132f1832b1427acb3748bc..bd0f4d77260f5b123856fc7e72d5f8e74bb45321 100644 +--- a/net/minecraft/world/entity/monster/Illusioner.java ++++ b/net/minecraft/world/entity/monster/Illusioner.java +@@ -57,10 +57,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()); +@@ -69,6 +87,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/MagmaCube.java b/net/minecraft/world/entity/monster/MagmaCube.java +index 905ecbd8b22c785ee4ea18004ac50eb1b7005d3f..f10b204c18b88e9110cebf050b60c23367ea3aa0 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(entityType, 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); + } +@@ -71,6 +93,7 @@ public class MagmaCube extends Slime { + float f = this.getSize() * 0.1F; + this.setDeltaMovement(deltaMovement.x, this.getJumpPower() + f, deltaMovement.z); + this.hasImpulse = 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 9ea3acd5ff3d7751875d61861aa5f6c717d0b5e2..75c6a43a3ab4851a47990402bee49f7e8305cd60 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/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 - 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; ++ } ++ ++ @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.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; +@@ -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 - 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()); + } + +@@ -90,6 +150,7 @@ public class Phantom extends FlyingMob 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()); + } + +@@ -147,6 +208,7 @@ public class Phantom extends FlyingMob implements Enemy { + @Override + public void aiStep() { + if (this.isAlive() && this.shouldBurnInDay && this.isSunBurnTick()) { // Paper - shouldBurnInDay API ++ if (getRider() == null || !this.isControllable()) // Purpur - Ridables + this.igniteForSeconds(8.0F); + } + +@@ -411,25 +473,42 @@ public class Phantom extends FlyingMob 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/Pillager.java b/net/minecraft/world/entity/monster/Pillager.java +index e855ebc5be2cef3b96e2c01a8c1d388e433c0d52..4e799981f04cd17a34f043dda82869adcf16ea98 100644 +--- a/net/minecraft/world/entity/monster/Pillager.java ++++ b/net/minecraft/world/entity/monster/Pillager.java +@@ -63,16 +63,35 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve + super(entityType, 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/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java +index 6f0fad37a05e9cd53b6e15c119127da492737c95..fb4e91c4f37619ce273ada0909932b32ba3b53f5 100644 +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -66,15 +66,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.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())); +@@ -131,7 +156,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 3f2668c79dd3d9e7973c1bba3e424b8220749682..e0a496a0c584e1f90967a8528a73536fd991e774 100644 +--- a/net/minecraft/world/entity/monster/Shulker.java ++++ b/net/minecraft/world/entity/monster/Shulker.java +@@ -104,12 +104,31 @@ public class Shulker extends AbstractGolem implements VariantHolder(this, Player.class, true)); + } +diff --git a/net/minecraft/world/entity/monster/Skeleton.java b/net/minecraft/world/entity/monster/Skeleton.java +index f0022458e3e01f6d01df0f8d69b2db73c77fb914..d4426daf3b8079a7e769013d43f44c72b0fce697 100644 +--- a/net/minecraft/world/entity/monster/Skeleton.java ++++ b/net/minecraft/world/entity/monster/Skeleton.java +@@ -25,6 +25,23 @@ public class Skeleton extends AbstractSkeleton { + super(entityType, 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/Slime.java b/net/minecraft/world/entity/monster/Slime.java +index 8db4cba1be6d7a5538295ba8da1fdaf7a77a16d0..7d31d68ac0fce102af480a47db73409926611428 100644 +--- a/net/minecraft/world/entity/monster/Slime.java ++++ b/net/minecraft/world/entity/monster/Slime.java +@@ -57,6 +57,7 @@ public class Slime extends Mob implements Enemy { + public float oSquish; + private boolean wasOnGround; + private boolean canWander = true; // Paper - Slime pathfinder events ++ protected boolean actualJump; // Purpur - Ridables + + public Slime(EntityType entityType, Level level) { + super(entityType, level); +@@ -64,12 +65,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)); +@@ -371,6 +408,7 @@ public class Slime extends Mob implements Enemy { + Vec3 deltaMovement = this.getDeltaMovement(); + this.setDeltaMovement(deltaMovement.x, this.getJumpPower(), deltaMovement.z); + this.hasImpulse = true; ++ this.actualJump = false; // Purpur - Ridables + } + + @Nullable +@@ -535,7 +573,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; +@@ -553,21 +591,33 @@ public class Slime extends Mob implements Enemy { + } + + public void setWantedMovement(double speed) { +- this.speedModifier = speed; ++ this.setSpeedModifier(speed); // 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) { +@@ -584,7 +634,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/Spider.java b/net/minecraft/world/entity/monster/Spider.java +index af0305079a367899708ee2bbac82aefaa9129d2f..ea83335dd0d128b32d2fe513eab82e642b533b4c 100644 +--- a/net/minecraft/world/entity/monster/Spider.java ++++ b/net/minecraft/world/entity/monster/Spider.java +@@ -50,15 +50,34 @@ public class Spider extends Monster { + super(entityType, 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/Stray.java b/net/minecraft/world/entity/monster/Stray.java +index 5fa2b7920a233afb3659b02cbd7ab82307ea9aaf..ed7ba19870a09ac78c1f069040a25e47c4b19d3a 100644 +--- a/net/minecraft/world/entity/monster/Stray.java ++++ b/net/minecraft/world/entity/monster/Stray.java +@@ -22,6 +22,23 @@ public class Stray extends AbstractSkeleton { + super(entityType, 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/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index ce690b564ea8ee055823928169fe605893498f3d..78671f02ef28f4a3b796b357d21fb4c9b64c153e 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -94,6 +94,23 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { + 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 + ) { +@@ -156,6 +173,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { + @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); +@@ -437,7 +455,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { + InteractionResult interactionResult = super.mobInteract(player, hand); + if (!interactionResult.consumesAction()) { + ItemStack itemInHand = player.getItemInHand(hand); +- return (InteractionResult)(itemInHand.is(Items.SADDLE) ? itemInHand.interactLivingEntity(player, this, hand) : InteractionResult.PASS); ++ return (InteractionResult)(itemInHand.is(Items.SADDLE) ? itemInHand.interactLivingEntity(player, this, hand) : 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 7f1cdea810db24182f8f87076c42a19b1b43e98a..26528bc9a9cffb68f82917a3e70900cfb65304d7 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(float 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 +@@ -301,13 +347,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(); +@@ -315,7 +361,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/Vindicator.java b/net/minecraft/world/entity/monster/Vindicator.java +index 5e7506f64159ac4838eee8594c995387e2fceed0..c1a1bb0be8bc77a1c0f771924f3bb8b4936d367b 100644 +--- a/net/minecraft/world/entity/monster/Vindicator.java ++++ b/net/minecraft/world/entity/monster/Vindicator.java +@@ -55,15 +55,34 @@ public class Vindicator extends AbstractIllager { + super(entityType, 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/Witch.java b/net/minecraft/world/entity/monster/Witch.java +index 9f5676b5fa0f369adb8643391738c5ae33911df7..0b3c78e646d68ef57a7cf5d7eb77a07c497bd216 100644 +--- a/net/minecraft/world/entity/monster/Witch.java ++++ b/net/minecraft/world/entity/monster/Witch.java +@@ -56,6 +56,23 @@ public class Witch extends Raider implements RangedAttackMob { + super(entityType, 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(); +@@ -64,10 +81,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/WitherSkeleton.java b/net/minecraft/world/entity/monster/WitherSkeleton.java +index eed8dbefd4d04082dc4e091c858e50309ed5c49b..b0f155564b11ff5fd2430694b937b7826df104ea 100644 +--- a/net/minecraft/world/entity/monster/WitherSkeleton.java ++++ b/net/minecraft/world/entity/monster/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/Zoglin.java b/net/minecraft/world/entity/monster/Zoglin.java +index 9b94e74f6317f835500225b087fe93487a7a0b22..b279e33bb14dfea4813bba770daf950f5343419d 100644 +--- a/net/minecraft/world/entity/monster/Zoglin.java ++++ b/net/minecraft/world/entity/monster/Zoglin.java +@@ -85,6 +85,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); +@@ -250,6 +267,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/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java +index 844add05c2a51825d410da428fd3e5a2ae71652c..9f476680247f50ca9381a4919dadc15f210a543c 100644 +--- a/net/minecraft/world/entity/monster/Zombie.java ++++ b/net/minecraft/world/entity/monster/Zombie.java +@@ -100,11 +100,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/ZombieVillager.java b/net/minecraft/world/entity/monster/ZombieVillager.java +index 3608fbcd1998ddcdec8ec501dd5f6b80911104ee..33bb29bc03bce90750b3b9376a6ed848208a569d 100644 +--- a/net/minecraft/world/entity/monster/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/ZombieVillager.java +@@ -78,6 +78,23 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { + .ifPresent(profession -> this.setVillagerData(this.getVillagerData().setProfession(profession.value()))); + } + ++ // 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/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +index f1e25786ef687b4680db1cca96a5ae6068e93946..369f1224ea787ae034d86d0e92696882304cb271 100644 +--- a/net/minecraft/world/entity/monster/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +@@ -63,6 +63,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 + public void setPersistentAngerTarget(@Nullable UUID target) { + this.persistentAngerTarget = target; +diff --git a/net/minecraft/world/entity/monster/creaking/Creaking.java b/net/minecraft/world/entity/monster/creaking/Creaking.java +index eba1e78352f956618b2796ce7cbe5d6f7e6591b6..57ac66c2de97c9b5940c1f0af663a1a26d2c8b73 100644 +--- a/net/minecraft/world/entity/monster/creaking/Creaking.java ++++ b/net/minecraft/world/entity/monster/creaking/Creaking.java +@@ -102,6 +102,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); +@@ -580,28 +603,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 0ddc0fe06a1b701f88ed8f8041ecd68f7da6c86d..028e09e1d8a14d989b2c19ca62e6544a93e1f1c4 100644 +--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -92,6 +92,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; +@@ -160,6 +177,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()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // 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/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java +index 0257eada48b35ea024520afe30596beae8a7ef1e..02d748ecb10c3e20aafc0c449b99ca5b6cd80e04 100644 +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -151,6 +151,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 + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); +@@ -346,6 +363,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()) && this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish // 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 0964b138e87357b7601ddfe937a2b9132afd5478..97241682311797faa93927e0477a7646ce53b2c8 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) +@@ -117,6 +134,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/warden/Warden.java b/net/minecraft/world/entity/monster/warden/Warden.java +index 9f476e587d7df797129e49738f101cccca7e10b7..f968e5c99bdb23b268bc34ea1ba5d54ae9ad0ff9 100644 +--- a/net/minecraft/world/entity/monster/warden/Warden.java ++++ b/net/minecraft/world/entity/monster/warden/Warden.java +@@ -129,8 +129,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); +@@ -394,6 +418,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/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java +index e6a2190e784cc3ebed528fb301d5263e3e821358..ffcb621de492461d33b748a6e27eec8ffc9e2c38 100644 +--- a/net/minecraft/world/entity/npc/Villager.java ++++ b/net/minecraft/world/entity/npc/Villager.java +@@ -246,6 +246,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(); +@@ -355,7 +377,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + // Purpur end - Lobotomize stuck villagers + // Pufferfish start +- if (!inactive /*&& this.behaviorTick++ % this.activatedPriority == 0*/) { ++ if (!inactive && (getRider() == null || !this.isControllable()) /*&& this.behaviorTick++ % this.activatedPriority == 0*/) { // Purpur - Ridables + this.getBrain().tick(level, this); // Paper - EAR 2 + } + // Pufferfish end +@@ -414,7 +436,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(); +@@ -427,9 +449,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.java b/net/minecraft/world/entity/npc/WanderingTrader.java +index fab309dc34eb88f2b9c844078f167885121675c1..0f8ec3abead11c46205cd21290c65ec2b859efdc 100644 +--- a/net/minecraft/world/entity/npc/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/WanderingTrader.java +@@ -76,6 +76,23 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill + } + // 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)); +@@ -137,8 +154,9 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill + + 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 7ce6d81b1b1dff25e8986d2c7a0c90afd2855f42..d0321875d2a2b612b438cc7973a7e9f172a61778 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -220,6 +220,19 @@ public abstract class Player extends LivingEntity { + } + // 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, BlockPos pos, float yRot, GameProfile gameProfile) { + super(EntityType.PLAYER, level); + this.setUUID(gameProfile.getId()); +diff --git a/net/minecraft/world/entity/projectile/LlamaSpit.java b/net/minecraft/world/entity/projectile/LlamaSpit.java +index 4880db97135d54fa72f64c108b2bd4ded096438b..bc102b049047d6e2a1d29e10f92cdf5ae2c140bd 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/WitherSkull.java b/net/minecraft/world/entity/projectile/WitherSkull.java +index c7a76d45b5749cf054607808610eb710493f80ea..9af37bd40649f602d700fc7b683c646ae9189eb9 100644 +--- a/net/minecraft/world/entity/projectile/WitherSkull.java ++++ b/net/minecraft/world/entity/projectile/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 0000000000..0e68624d50 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0002-Configurable-entity-base-attributes.patch @@ -0,0 +1,1788 @@ +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/GlowSquid.java b/net/minecraft/world/entity/GlowSquid.java +index d0313fd5368baa53ec511c8c07fc78a1f1ecec4e..898b1e01026ec1f44cfe60e9f18a997c86e30594 100644 +--- a/net/minecraft/world/entity/GlowSquid.java ++++ b/net/minecraft/world/entity/GlowSquid.java +@@ -45,6 +45,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/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index 2de776d7570f97f763033392ceeb8ffdbc0fafcd..b96fc67d28809f66181b1d36b475a8f85d596ea7 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -311,6 +311,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + protected LivingEntity(EntityType entityType, Level level) { + super(entityType, level); + this.attributes = new AttributeMap(DefaultAttributes.getSupplier(entityType), this); // Purpur - Ridables ++ this.initAttributes(); // Purpur - Configurable entity base attributes + this.craftAttributes = new 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()); +@@ -324,6 +325,8 @@ public abstract class LivingEntity extends Entity implements Attackable { + this.brain = this.makeBrain(new Dynamic<>(nbtOps, nbtOps.createMap(ImmutableMap.of(nbtOps.createString("memories"), nbtOps.emptyMap())))); + } + ++ 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 e7ea944e77175ee4051b8e7361c502d0cc2115d5..ecbec552e5cd1935f57872d2fb502d3e9743e3d8 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/Bee.java b/net/minecraft/world/entity/animal/Bee.java +index b9bf40c389460d65d2566786ddd6f6740243e9a4..c150ba5f706b3dd51925533300c0432ccf5e2b81 100644 +--- a/net/minecraft/world/entity/animal/Bee.java ++++ b/net/minecraft/world/entity/animal/Bee.java +@@ -472,6 +472,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 int getRemainingPersistentAngerTime() { + return this.entityData.get(DATA_REMAINING_ANGER_TIME); +diff --git a/net/minecraft/world/entity/animal/Cat.java b/net/minecraft/world/entity/animal/Cat.java +index f066b0acfa0e954f6d71e62962c76afa1f05a4a5..98ce277c5b27591e22daa3c85241be1b8689bfae 100644 +--- a/net/minecraft/world/entity/animal/Cat.java ++++ b/net/minecraft/world/entity/animal/Cat.java +@@ -118,6 +118,14 @@ public class Cat extends TamableAnimal implements VariantHolder itemStack.is(ItemTags.CAT_FOOD), true); +diff --git a/net/minecraft/world/entity/animal/Chicken.java b/net/minecraft/world/entity/animal/Chicken.java +index aba1bf732bb78a24dba1f063d65894fde92789ef..509163f409a5b8988a484aedb2f3ddf042d5eb13 100644 +--- a/net/minecraft/world/entity/animal/Chicken.java ++++ b/net/minecraft/world/entity/animal/Chicken.java +@@ -68,6 +68,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/Cod.java b/net/minecraft/world/entity/animal/Cod.java +index 6a19086e272363701260801f3c6db9b5c91b8ef5..434e1fabf2e360a8f5f4eefed96e3883aa786d10 100644 +--- a/net/minecraft/world/entity/animal/Cod.java ++++ b/net/minecraft/world/entity/animal/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/Cow.java b/net/minecraft/world/entity/animal/Cow.java +index 656babc0c8810a85eb9f78ced1f3ad9551fdc286..d2a4bfa5334f7361067e4adac36ba5a4a4fa6ad8 100644 +--- a/net/minecraft/world/entity/animal/Cow.java ++++ b/net/minecraft/world/entity/animal/Cow.java +@@ -55,6 +55,14 @@ public class Cow extends Animal { + } + // 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 registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/Dolphin.java b/net/minecraft/world/entity/animal/Dolphin.java +index 35bce598bb5857356823594d2a001006ce19f835..5b764c686e8759a7b04a7b50708c69629be02c04 100644 +--- a/net/minecraft/world/entity/animal/Dolphin.java ++++ b/net/minecraft/world/entity/animal/Dolphin.java +@@ -148,6 +148,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 ++ + @Nullable + @Override + public SpawnGroupData finalizeSpawn( +diff --git a/net/minecraft/world/entity/animal/Fox.java b/net/minecraft/world/entity/animal/Fox.java +index 8b0a813f9dd001c6dd108ba7aac04d134a20fbc1..8bf893837586ae2a9b4ef7564d242e16e4863b5d 100644 +--- a/net/minecraft/world/entity/animal/Fox.java ++++ b/net/minecraft/world/entity/animal/Fox.java +@@ -167,6 +167,14 @@ public class Fox extends Animal implements VariantHolder { + } + // 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/IronGolem.java b/net/minecraft/world/entity/animal/IronGolem.java +index 223c4796f659a24062a719045e484a22d31ab2f0..37a353cbb0e9b16e0fc92bd1bc8194cb4cd3c13a 100644 +--- a/net/minecraft/world/entity/animal/IronGolem.java ++++ b/net/minecraft/world/entity/animal/IronGolem.java +@@ -90,6 +90,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/MushroomCow.java b/net/minecraft/world/entity/animal/MushroomCow.java +index 1292146341022483f78a9128ef9d7a88089274a0..990723c31aa1040a4e45b9857a18d86287ef91b4 100644 +--- a/net/minecraft/world/entity/animal/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/MushroomCow.java +@@ -72,6 +72,13 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { + } + // 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/Salmon.java b/net/minecraft/world/entity/animal/Salmon.java +index ebbd6d39c3f5d6c66445c2c743785ed369408389..93eb3cc3605f694337c1604e2db63fed04693617 100644 +--- a/net/minecraft/world/entity/animal/Salmon.java ++++ b/net/minecraft/world/entity/animal/Salmon.java +@@ -47,6 +47,13 @@ public class Salmon extends AbstractSchoolingFish implements VariantHolder 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 87a190d8646d8bbed8c182f9f0f7d8c398e63d26..c10ebb66dec26b6ccc223e98effa0b9a68363626 100644 +--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java ++++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java +@@ -97,6 +97,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 2054e4624da0c9b04ea69b9bf39443c4574d48be..f2f09a529e9db88784ff4299fdf3966046c736ab 100644 +--- a/net/minecraft/world/entity/animal/axolotl/Axolotl.java ++++ b/net/minecraft/world/entity/animal/axolotl/Axolotl.java +@@ -132,6 +132,14 @@ public class Axolotl extends Animal implements VariantHolder, B + } + // 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/camel/Camel.java b/net/minecraft/world/entity/animal/camel/Camel.java +index 11311d2ec37d825e73e2218e60e2606dd3a25a1d..1d7e2358bac193af48dc4b7f5b0295e3bffa152b 100644 +--- a/net/minecraft/world/entity/animal/camel/Camel.java ++++ b/net/minecraft/world/entity/animal/camel/Camel.java +@@ -322,6 +322,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 + protected SoundEvent getAmbientSound() { + return SoundEvents.CAMEL_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/net/minecraft/world/entity/animal/horse/AbstractHorse.java +index 828406060e50ff62586929371aafb46ef7d81f92..56dc7011ed07f0bd5870fbadde2b5c0c630c5edd 100644 +--- a/net/minecraft/world/entity/animal/horse/AbstractHorse.java ++++ b/net/minecraft/world/entity/animal/horse/AbstractHorse.java +@@ -218,6 +218,46 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, + } + // 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 +@@ -1218,7 +1258,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, + 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/horse/Donkey.java b/net/minecraft/world/entity/animal/horse/Donkey.java +index ee3fa710e95f2e84f7f9bdce1159d1136815172d..223f1d109680e3643ab2c8343be22713e89755fd 100644 +--- a/net/minecraft/world/entity/animal/horse/Donkey.java ++++ b/net/minecraft/world/entity/animal/horse/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 + protected SoundEvent getAmbientSound() { + return SoundEvents.DONKEY_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/horse/Horse.java b/net/minecraft/world/entity/animal/horse/Horse.java +index 361bf346153912bcbfcf962d7f716dfe12ae2a7b..8bd118e82da9e4d4153de0a3efaf6d69e3c4c540 100644 +--- a/net/minecraft/world/entity/animal/horse/Horse.java ++++ b/net/minecraft/world/entity/animal/horse/Horse.java +@@ -50,6 +50,23 @@ public class Horse extends AbstractHorse implements VariantHolder { + } + // 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/horse/Llama.java b/net/minecraft/world/entity/animal/horse/Llama.java +index 164a429d432badcb315e8ece406e29e576a11265..58e726dd33f572a31b4910b9ff666c4252fb03a9 100644 +--- a/net/minecraft/world/entity/animal/horse/Llama.java ++++ b/net/minecraft/world/entity/animal/horse/Llama.java +@@ -124,6 +124,23 @@ public class Llama extends AbstractChestedHorse implements VariantHolder entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/Illusioner.java b/net/minecraft/world/entity/monster/Illusioner.java +index bd0f4d77260f5b123856fc7e72d5f8e74bb45321..1d1cf8748e3fba2e2963ad2fa153fbfe990f5087 100644 +--- a/net/minecraft/world/entity/monster/Illusioner.java ++++ b/net/minecraft/world/entity/monster/Illusioner.java +@@ -74,6 +74,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/MagmaCube.java b/net/minecraft/world/entity/monster/MagmaCube.java +index f10b204c18b88e9110cebf050b60c23367ea3aa0..2c6b0fd46d9ed6a8d1ca7e90ebf596dd3f310f0e 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 75c6a43a3ab4851a47990402bee49f7e8305cd60..08fc2dc0fecfa370c99e877d502149a8ea147e5f 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -151,7 +151,10 @@ public class Phantom extends FlyingMob 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() { +@@ -176,6 +179,23 @@ public class Phantom extends FlyingMob implements Enemy { + return true; + } + ++ // 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/Pillager.java b/net/minecraft/world/entity/monster/Pillager.java +index 4e799981f04cd17a34f043dda82869adcf16ea98..9586aa3f3eb61fb0c1224df9d0104da69d7fa6bb 100644 +--- a/net/minecraft/world/entity/monster/Pillager.java ++++ b/net/minecraft/world/entity/monster/Pillager.java +@@ -80,6 +80,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/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java +index 09910d526cdf3484474463ee4ea1ca8501280e45..55ddffbea1b86fa0fd5c5f435a5f7633702c3e5b 100644 +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -89,6 +89,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 e0a496a0c584e1f90967a8528a73536fd991e774..03db684c122a07176aa1365550da935cdb66a1b9 100644 +--- a/net/minecraft/world/entity/monster/Shulker.java ++++ b/net/minecraft/world/entity/monster/Shulker.java +@@ -121,6 +121,14 @@ 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 - Configurable entity base attributes ++ + @Override + protected void registerGoals() { + this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables +@@ -129,9 +162,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/Spider.java b/net/minecraft/world/entity/monster/Spider.java +index ea83335dd0d128b32d2fe513eab82e642b533b4c..38d75a0a024fa1e7b12bfc5e3ab0ec8bb98cb17a 100644 +--- a/net/minecraft/world/entity/monster/Spider.java ++++ b/net/minecraft/world/entity/monster/Spider.java +@@ -67,6 +67,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/Stray.java b/net/minecraft/world/entity/monster/Stray.java +index ed7ba19870a09ac78c1f069040a25e47c4b19d3a..0323456fca18450c22bf3999df97ff148a89e4c5 100644 +--- a/net/minecraft/world/entity/monster/Stray.java ++++ b/net/minecraft/world/entity/monster/Stray.java +@@ -39,6 +39,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/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index 78671f02ef28f4a3b796b357d21fb4c9b64c153e..be0dc92bf5ae3da1368a649e9c4e7ff5dbb1c67c 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -111,6 +111,14 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { + } + // 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 26528bc9a9cffb68f82917a3e70900cfb65304d7..8356906b2c0707e21021bb05f9ca01a95682880a 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/Vindicator.java b/net/minecraft/world/entity/monster/Vindicator.java +index c1a1bb0be8bc77a1c0f771924f3bb8b4936d367b..0fc1b458101ba9d98d25c9637337caf0949bb893 100644 +--- a/net/minecraft/world/entity/monster/Vindicator.java ++++ b/net/minecraft/world/entity/monster/Vindicator.java +@@ -72,6 +72,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/Witch.java b/net/minecraft/world/entity/monster/Witch.java +index 0b3c78e646d68ef57a7cf5d7eb77a07c497bd216..ff8380246f6c6c805b222a91ac6a1eb0d130558d 100644 +--- a/net/minecraft/world/entity/monster/Witch.java ++++ b/net/minecraft/world/entity/monster/Witch.java +@@ -73,6 +73,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/WitherSkeleton.java b/net/minecraft/world/entity/monster/WitherSkeleton.java +index b0f155564b11ff5fd2430694b937b7826df104ea..3342f2d92830049837636ff10b5e52f0d85fbd2c 100644 +--- a/net/minecraft/world/entity/monster/WitherSkeleton.java ++++ b/net/minecraft/world/entity/monster/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/Zoglin.java b/net/minecraft/world/entity/monster/Zoglin.java +index b279e33bb14dfea4813bba770daf950f5343419d..132b38d717ac3c5acc64a5ec519f345ac57021d8 100644 +--- a/net/minecraft/world/entity/monster/Zoglin.java ++++ b/net/minecraft/world/entity/monster/Zoglin.java +@@ -102,6 +102,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/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java +index 9f476680247f50ca9381a4919dadc15f210a543c..73dfc60d9d30ce53a75eaac2630cdd9a8c508ccc 100644 +--- a/net/minecraft/world/entity/monster/Zombie.java ++++ b/net/minecraft/world/entity/monster/Zombie.java +@@ -117,6 +117,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 +@@ -631,7 +639,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 + } + + @Override +diff --git a/net/minecraft/world/entity/monster/ZombieVillager.java b/net/minecraft/world/entity/monster/ZombieVillager.java +index 33bb29bc03bce90750b3b9376a6ed848208a569d..578cfc33a493b5ebc2ed42733577129a8953a461 100644 +--- a/net/minecraft/world/entity/monster/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/ZombieVillager.java +@@ -95,6 +95,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/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +index 369f1224ea787ae034d86d0e92696882304cb271..1424954f5b4cf0fbe821425cd741b4b5c1bfed50 100644 +--- a/net/minecraft/world/entity/monster/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +@@ -80,6 +80,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 + public void setPersistentAngerTarget(@Nullable UUID target) { + this.persistentAngerTarget = target; +@@ -262,7 +270,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 + } + + @Nullable +diff --git a/net/minecraft/world/entity/monster/creaking/Creaking.java b/net/minecraft/world/entity/monster/creaking/Creaking.java +index 57ac66c2de97c9b5940c1f0af663a1a26d2c8b73..887a81ea82b86edceaa46eb2032f53fccb84e158 100644 +--- a/net/minecraft/world/entity/monster/creaking/Creaking.java ++++ b/net/minecraft/world/entity/monster/creaking/Creaking.java +@@ -125,6 +125,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 028e09e1d8a14d989b2c19ca62e6544a93e1f1c4..54924cd7c84cbcd22ffc0bd37fc24f24e73c18bc 100644 +--- a/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -109,6 +109,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/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java +index 02d748ecb10c3e20aafc0c449b99ca5b6cd80e04..897c57263ab7347987b289016a71d11f693bc8b2 100644 +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -168,6 +168,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 + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); +diff --git a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +index 97241682311797faa93927e0477a7646ce53b2c8..eb82252cd87797927e153974b9280b5eaa251080 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/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java +index 52b54c59efd68a955a6e7cc49b01f614043c505d..c71d0f8efacb60e49395567fdc0c1c1e6e6f5aa8 100644 +--- a/net/minecraft/world/entity/npc/Villager.java ++++ b/net/minecraft/world/entity/npc/Villager.java +@@ -268,6 +268,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.java b/net/minecraft/world/entity/npc/WanderingTrader.java +index 7e4d14d30eb3f06c0c7426e09084355ab4f3857d..b5af32a431b5ffe20b32bd82ccfae9b8343d0592 100644 +--- a/net/minecraft/world/entity/npc/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/WanderingTrader.java +@@ -93,6 +93,13 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill + } + // 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 0000000000..c44228c21f --- /dev/null +++ b/purpur-server/minecraft-patches/features/0003-Barrels-and-enderchests-6-rows.patch @@ -0,0 +1,219 @@ +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 2d09f2a2c97f29ac0d941b7a3fb941102a5d545e..94abb9d8f6381aee000dbd0720477db8b7ca279c 100644 +--- a/net/minecraft/server/players/PlayerList.java ++++ b/net/minecraft/server/players/PlayerList.java +@@ -1027,6 +1027,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 + } + + public boolean isWhiteListed(GameProfile profile) { +diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java +index d0321875d2a2b612b438cc7973a7e9f172a61778..15308ff3ed5835e3b8f41e7ddc2045f424e14660 100644 +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -202,6 +202,7 @@ public abstract class Player extends LivingEntity { + 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 280169afbd637eeb67ddf7eaeb4eecd464a128d5..ba7730a24831efa33de4c5ffce57bfa7177f89d6 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 a6a359bab2a727f4631b633a8bb370dd40decc75..d2d75e5c34c97300ce5da8c7ea70958aba31fa4a 100644 +--- a/net/minecraft/world/inventory/PlayerEnderChestContainer.java ++++ b/net/minecraft/world/inventory/PlayerEnderChestContainer.java +@@ -25,11 +25,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 f5533960708bdbaf2eacefbc7c7c3123b7d26502..17aa27885b4431bf7b98799e02d080b5a0ecbbf1 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 0f808855f58281578c2758513787f0f7330c9291..9f6063089f0aa3a68d26ae7cfe39379123ab2f47 100644 +--- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java +@@ -55,7 +55,17 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { + this.maxStack = i; + } + // CraftBukkit end +- 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) { +@@ -107,7 +117,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 +@@ -127,7 +146,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/patches/server/0023-Giants-AI-settings.patch b/purpur-server/minecraft-patches/features/0004-Giants-AI-settings.patch similarity index 64% rename from patches/server/0023-Giants-AI-settings.patch rename to purpur-server/minecraft-patches/features/0004-Giants-AI-settings.patch index 8bfa51d6ed..0418b73492 100644 --- a/patches/server/0023-Giants-AI-settings.patch +++ b/purpur-server/minecraft-patches/features/0004-Giants-AI-settings.patch @@ -4,16 +4,17 @@ 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 4a13274a863345650ca0c3f98d6b2735221c778a..adc90a306fc9023f4a1cdfe9f58c39726086c849 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Giant.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Giant.java -@@ -30,8 +30,23 @@ public class Giant extends Monster { +diff --git a/net/minecraft/world/entity/monster/Giant.java b/net/minecraft/world/entity/monster/Giant.java +index 13021800af7cc9263ef4f393f9cfbda5061a32ae..73da18c4b54e250c434fd75971ef0a8f7c8cf6a3 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)); @@ -31,13 +32,15 @@ index 4a13274a863345650ca0c3f98d6b2735221c778a..adc90a306fc9023f4a1cdfe9f58c3972 + this.targetSelector.addGoal(5, new net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal<>(this, net.minecraft.world.entity.animal.Turtle.class, true)); + } + } ++ // Purpur end - Giants AI settings } // Purpur end - Ridables -@@ -48,8 +63,34 @@ public class Giant extends Monster { +@@ -49,8 +66,36 @@ public class Giant extends Monster { return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 100.0).add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.ATTACK_DAMAGE, 50.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); @@ -63,36 +66,11 @@ index 4a13274a863345650ca0c3f98d6b2735221c778a..adc90a306fc9023f4a1cdfe9f58c3972 + // 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 world) { -- return world.getPathfindingCostFromLightLevels(pos); -+ return super.getWalkTargetValue(pos, world); // Purpur - fix light requirements for natural spawns + 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/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index f6186c65468953756da9c381c6ee712115a244f7..3c3df7767bc97882f53f32de1b4a350f73b16f23 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -635,6 +635,10 @@ public class PurpurWorldConfig { - 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; - private void giantSettings() { - giantRidable = getBoolean("mobs.giant.ridable", giantRidable); - giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater); -@@ -652,6 +656,10 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean glowSquidRidable = false; 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 0000000000..9156be5d53 --- /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.java b/net/minecraft/world/entity/animal/Chicken.java +index 509163f409a5b8988a484aedb2f3ddf042d5eb13..d718f0ed362c49803260dceed64bd93e2b6744fc 100644 +--- a/net/minecraft/world/entity/animal/Chicken.java ++++ b/net/minecraft/world/entity/animal/Chicken.java +@@ -73,6 +73,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 + +@@ -80,13 +85,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 +@@ -95,7 +108,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 0000000000..dd6a74fc4b --- /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 580818b9d2f992dd33b4831dbc612ba28c1bd725..3c7a30c9d1aa00f2b8e25c505ec57ad27344ffe5 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -1240,6 +1240,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 net.minecraft.world.entity.vehicle.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 && !level().purpurConfig.boatsDoFallDamage) { + return false; + } +diff --git a/net/minecraft/world/entity/vehicle/AbstractMinecart.java b/net/minecraft/world/entity/vehicle/AbstractMinecart.java +index 9e15e7159cf98b3928110df9eae6de93793bf44e..6df4d736d94b9e49a3eb3d59a329e37127aa64cd 100644 +--- a/net/minecraft/world/entity/vehicle/AbstractMinecart.java ++++ b/net/minecraft/world/entity/vehicle/AbstractMinecart.java +@@ -83,6 +83,10 @@ public abstract class AbstractMinecart extends VehicleEntity { + private double flyingY = 0.95; + private double flyingZ = 0.95; + public 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 + +@@ -91,8 +95,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 + } + } + +@@ -258,6 +267,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(); +@@ -394,15 +411,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 620069daba04d48b57fc933328eda77f6ca9333e..0403b9b01994269d394820e8c8710ba1b9808bf0 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 0000000000..64b658e5bd --- /dev/null +++ b/purpur-server/minecraft-patches/features/0007-Villagers-follow-emerald-blocks.patch @@ -0,0 +1,103 @@ +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 61ed4d687120fcbb7b91863e400f3657ebcde687..e773c426567964fc8269237d71c3434a5473985c 100644 +--- a/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java ++++ b/net/minecraft/world/entity/ai/attributes/DefaultAttributes.java +@@ -162,7 +162,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.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 438d6347778a94b4fe430320b268a2d67afa209a..f88f618d34fb343b31de3af1a875d6633703df71 100644 +--- a/net/minecraft/world/entity/ai/goal/TemptGoal.java ++++ b/net/minecraft/world/entity/ai/goal/TemptGoal.java +@@ -58,7 +58,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 - Villagers follow emerald blocks + } + + @Override +diff --git a/net/minecraft/world/entity/npc/AbstractVillager.java b/net/minecraft/world/entity/npc/AbstractVillager.java +index a71d16d968bb90fd7aca6f01a3dd56df4f9a7ce6..b4e79cac5611942240ce85120f7bbee329ae2fb8 100644 +--- a/net/minecraft/world/entity/npc/AbstractVillager.java ++++ b/net/minecraft/world/entity/npc/AbstractVillager.java +@@ -45,6 +45,8 @@ 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 - Villagers follow emerald blocks ++ + // CraftBukkit start + @Override + public CraftMerchant getCraftMerchant() { +diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java +index c71d0f8efacb60e49395567fdc0c1c1e6e6f5aa8..2f685a186b2dc27e70cddd5c4951c27e7ee3ef53 100644 +--- a/net/minecraft/world/entity/npc/Villager.java ++++ b/net/minecraft/world/entity/npc/Villager.java +@@ -265,6 +265,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 + +@@ -273,6 +274,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 + +@@ -341,7 +343,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.java b/net/minecraft/world/entity/npc/WanderingTrader.java +index b5af32a431b5ffe20b32bd82ccfae9b8343d0592..f9755f36f7863b9742fe5b840a8130891ddff7c7 100644 +--- a/net/minecraft/world/entity/npc/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/WanderingTrader.java +@@ -97,9 +97,16 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill + @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)); +@@ -134,6 +141,7 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill + 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-Configurable-void-damage-height-and-damage.patch b/purpur-server/minecraft-patches/features/0008-Configurable-void-damage-height-and-damage.patch new file mode 100644 index 0000000000..839f90b9ec --- /dev/null +++ b/purpur-server/minecraft-patches/features/0008-Configurable-void-damage-height-and-damage.patch @@ -0,0 +1,49 @@ +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 + +temporarily migrate to paper's config +drop patch on the next minecraft release + +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index 2bd5a295be2500f198230a62f48900b51038d22b..96253bf49a6895524f6f606a9c434cb1b78948a6 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -1222,6 +1222,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop +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 d1f7da0f4adc4609247c349d7ccdb0e6bba9b8f8..6de5f527c018201d874e06a45c9509fa12125766 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -3595,7 +3595,18 @@ public abstract class LivingEntity extends Entity implements Attackable { + if (i1 % 2 == 0) { + List list = EquipmentSlot.VALUES.stream().filter(slot -> canGlideUsing(this.getItemBySlot(slot), slot)).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 75a9bd205f32b77c5d242cb9fac0f571ce36045a..b03f182c62c699cc222e67c1ae6eadf99c45d48d 100644 +--- a/net/minecraft/world/item/FireworkRocketItem.java ++++ b/net/minecraft/world/item/FireworkRocketItem.java +@@ -66,6 +66,19 @@ public class FireworkRocketItem extends Item implements ProjectileItem { + com.destroystokyo.paper.event.player.PlayerElytraBoostEvent event = new com.destroystokyo.paper.event.player.PlayerElytraBoostEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Firework) delayed.projectile().getBukkitEntity(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand)); + if (event.callEvent() && delayed.attemptSpawn()) { + player.awardStat(Stats.ITEM_USED.get(this)); // Moved up from below ++ ++ // Purpur start - Implement elytra settings ++ if (level.purpurConfig.elytraDamagePerFireworkBoost > 0) { ++ 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.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 c5426585f53a3139dc9d188a73e3a7ff4cb2e492..264b713e8b7c3d5f7d8e1facc90a60349f2cf414 100644 +--- a/net/minecraft/world/item/ItemStack.java ++++ b/net/minecraft/world/item/ItemStack.java +@@ -733,6 +733,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 07f8c7644a112bb1ba283d1eadd8661010e888a4..7ea7db834e7b627a1d7d37ca87cd43eb61408565 100644 +--- a/net/minecraft/world/item/TridentItem.java ++++ b/net/minecraft/world/item/TridentItem.java +@@ -133,6 +133,18 @@ public class TridentItem extends Item implements ProjectileItem { + f1 *= tridentSpinAttackStrength / squareRoot; + f2 *= tridentSpinAttackStrength / squareRoot; + org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerRiptideEvent(player, stack, f, f1, f2); // CraftBukkit ++ ++ // Purpur start - Implement elytra settings ++ List list = EquipmentSlot.VALUES.stream().filter((enumitemslot) -> LivingEntity.canGlideUsing(entity.getItemBySlot(enumitemslot), enumitemslot)).toList(); ++ if (!list.isEmpty()) { ++ EquipmentSlot enumitemslot = net.minecraft.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/0010-Configurable-jockey-options.patch b/purpur-server/minecraft-patches/features/0010-Configurable-jockey-options.patch new file mode 100644 index 0000000000..b97e69bbc5 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0010-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/Drowned.java b/net/minecraft/world/entity/monster/Drowned.java +index 3b0b5eadb80490b98b24aa632ab956e6403e2df2..173c0f13202e1ab3491ffde72ece95e2b4a46fd6 100644 +--- a/net/minecraft/world/entity/monster/Drowned.java ++++ b/net/minecraft/world/entity/monster/Drowned.java +@@ -105,6 +105,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/Husk.java b/net/minecraft/world/entity/monster/Husk.java +index c2365ae1cf6f98e262f302a117c4647c383dfbb5..7a8951f93e65c6df145e30d44b9d928dd0c39189 100644 +--- a/net/minecraft/world/entity/monster/Husk.java ++++ b/net/minecraft/world/entity/monster/Husk.java +@@ -50,6 +50,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 ++ + public static boolean checkHuskSpawnRules( + EntityType entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java +index 4b0f04c861397af694c477a8d3dc0de707e36874..b600fc46ed96d46769b7b289997a6e3cd422417b 100644 +--- a/net/minecraft/world/entity/monster/Zombie.java ++++ b/net/minecraft/world/entity/monster/Zombie.java +@@ -125,6 +125,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 +@@ -556,19 +570,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); +- } +- } else if (random.nextFloat() < 0.05) { ++ } else { // Purpur - Configurable jockey options + Chicken chicken1 = EntityType.CHICKEN.create(this.level(), EntitySpawnReason.JOCKEY); + if (chicken1 != null) { + chicken1.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F); +@@ -577,6 +590,7 @@ public class Zombie extends Monster { + this.startRiding(chicken1); + level.addFreshEntity(chicken1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.MOUNT); // CraftBukkit + } ++ } // Purpur - Configurable jockey options + } + } + } +diff --git a/net/minecraft/world/entity/monster/ZombieVillager.java b/net/minecraft/world/entity/monster/ZombieVillager.java +index 610e5e5330462646034c5667c15245fdb2af77a0..54df75681a89df93f59a589189c7e96b4acc4c77 100644 +--- a/net/minecraft/world/entity/monster/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/ZombieVillager.java +@@ -107,6 +107,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/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +index 486906b860b3ccbeb1702d9bd7a5d9f11f534b1a..eaa2682de228cf5e9aff22b15f045c9ecbeec77b 100644 +--- a/net/minecraft/world/entity/monster/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +@@ -88,6 +88,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 + public void setPersistentAngerTarget(@Nullable UUID target) { + this.persistentAngerTarget = target; diff --git a/patches/server/0064-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/purpur-server/minecraft-patches/features/0011-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch similarity index 71% rename from patches/server/0064-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch rename to purpur-server/minecraft-patches/features/0011-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch index 779ff35a8a..beeba7f4cd 100644 --- a/patches/server/0064-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch +++ b/purpur-server/minecraft-patches/features/0011-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch @@ -4,11 +4,11 @@ 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 7cb3d69a69e0e3ef4b7f9f9c8b1eb67edb5d116d..931cb90a8b32eb47d5985807d74d8ef7f1d01baf 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 -@@ -31,6 +31,12 @@ public class EndCrystal extends Entity { +diff --git a/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java b/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java +index 67711964552a8e32d3590a64aff78e1db768b026..d58829c88b86358a0c06a982b302fc9a31c15853 100644 +--- a/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java ++++ b/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java +@@ -26,6 +26,12 @@ public class EndCrystal extends Entity { private static final EntityDataAccessor DATA_SHOW_BOTTOM = SynchedEntityData.defineId(EndCrystal.class, EntityDataSerializers.BOOLEAN); public int time; public boolean generatedByDragonFight = false; // Paper - Fix invulnerable end crystals @@ -19,13 +19,13 @@ index 7cb3d69a69e0e3ef4b7f9f9c8b1eb67edb5d116d..931cb90a8b32eb47d5985807d74d8ef7 + private int idleCooldown = 0; + // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms - public EndCrystal(EntityType type, Level world) { - super(type, world); -@@ -82,6 +88,49 @@ public class EndCrystal extends Entity { - // Paper end - Fix invulnerable end crystals - } + public EndCrystal(EntityType entityType, Level level) { + super(entityType, level); +@@ -94,6 +100,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 - Phantoms attracted to crystals and crystals shoot phantoms ++ // Purpur start - Phantoms attracted to crystals and crystals shoot phantoms + if (level().purpurConfig.phantomAttackedByCrystalRadius <= 0 || --idleCooldown > 0) { + return; // on cooldown + } @@ -67,23 +67,23 @@ index 7cb3d69a69e0e3ef4b7f9f9c8b1eb67edb5d116d..931cb90a8b32eb47d5985807d74d8ef7 + phantomBeamTicks = 0; + phantomDamageCooldown = 0; + idleCooldown = 60; -+ // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms ++ // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms } @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 ee00c4c7a1e0f08cdeccab63a20c4b465fdeb549..093c2601ff561c47775f9f99cb5990e6c1b05d8c 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; +diff --git a/net/minecraft/world/entity/monster/Phantom.java b/net/minecraft/world/entity/monster/Phantom.java +index 08fc2dc0fecfa370c99e877d502149a8ea147e5f..aea7b608d88d243113f67665844841ac879c3f88 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -47,6 +47,7 @@ public class Phantom extends FlyingMob implements Enemy { + Vec3 moveTargetPoint = Vec3.ZERO; + public BlockPos anchorPoint = BlockPos.ZERO; + Phantom.AttackPhase attackPhase = Phantom.AttackPhase.CIRCLE; + Vec3 crystalPosition; // Purpur - Phantoms attracted to crystals and crystals shoot phantoms - - public Phantom(EntityType type, Level world) { - super(type, world); -@@ -118,6 +119,24 @@ public class Phantom extends FlyingMob implements Enemy { + // Paper start + @Nullable + public java.util.UUID spawningEntity; +@@ -118,6 +119,25 @@ public class Phantom extends FlyingMob implements Enemy { } // Purpur end - Ridables @@ -105,10 +105,11 @@ index ee00c4c7a1e0f08cdeccab63a20c4b465fdeb549..093c2601ff561c47775f9f99cb5990e6 + return crystalPosition != null; + } + // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms ++ @Override public boolean isFlapping() { - return (this.getUniqueFlapTickOffset() + this.tickCount) % Phantom.TICKS_PER_FLAP == 0; -@@ -131,9 +150,15 @@ public class Phantom extends FlyingMob implements Enemy { + return (this.getUniqueFlapTickOffset() + this.tickCount) % TICKS_PER_FLAP == 0; +@@ -131,9 +151,15 @@ public class Phantom extends FlyingMob implements Enemy { @Override protected void registerGoals() { this.goalSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables @@ -117,8 +118,8 @@ index ee00c4c7a1e0f08cdeccab63a20c4b465fdeb549..093c2601ff561c47775f9f99cb5990e6 - this.goalSelector.addGoal(3, new Phantom.PhantomCircleAroundAnchorGoal()); + // 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()); @@ -127,17 +128,17 @@ index ee00c4c7a1e0f08cdeccab63a20c4b465fdeb549..093c2601ff561c47775f9f99cb5990e6 this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables this.targetSelector.addGoal(1, new Phantom.PhantomAttackPlayerTargetGoal()); } -@@ -334,6 +359,123 @@ public class Phantom extends FlyingMob implements Enemy { - private AttackPhase() {} +@@ -509,6 +535,124 @@ public class Phantom extends FlyingMob implements Enemy { + } } + // Purpur start - Phantoms attracted to crystals and crystals shoot phantoms -+ class FindCrystalGoal extends Goal { ++ 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)); @@ -185,14 +186,14 @@ index ee00c4c7a1e0f08cdeccab63a20c4b465fdeb549..093c2601ff561c47775f9f99cb5990e6 + } + } + -+ 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)); + } @@ -248,30 +249,7 @@ index ee00c4c7a1e0f08cdeccab63a20c4b465fdeb549..093c2601ff561c47775f9f99cb5990e6 + } + } + // Purpur end - Phantoms attracted to crystals and crystals shoot phantoms - private class PhantomMoveControl extends org.purpurmc.purpur.controller.FlyingMoveControllerWASD { // Purpur - Ridables - ++ + 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 bb8aa3d4e33e2d8ed4bf3fd668577ea901410783..429c0bd678cb726b761e32d4b22b33c630f28097 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1163,6 +1163,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); -@@ -1184,6 +1187,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/0012-Phantoms-burn-in-light.patch b/purpur-server/minecraft-patches/features/0012-Phantoms-burn-in-light.patch new file mode 100644 index 0000000000..34631ae4b1 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0012-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 aea7b608d88d243113f67665844841ac879c3f88..4cc1c8d8967b1e9ee5b0b1c50d887f3de3e8a882 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -53,6 +53,7 @@ public class Phantom extends FlyingMob implements Enemy { + public java.util.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 entityType, Level level) { + super(entityType, level); +@@ -253,7 +254,11 @@ public class Phantom extends FlyingMob implements Enemy { + + @Override + public void aiStep() { +- if (this.isAlive() && this.shouldBurnInDay && this.isSunBurnTick()) { // Paper - shouldBurnInDay API ++ // Purpur start - Phantoms burn in light ++ boolean burnFromDaylight = this.shouldBurnInDay && this.isSunBurnTick() && 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 - shouldBurnInDay API ++ // Purpur end - Phantoms burn in light + if (getRider() == null || !this.isControllable()) // Purpur - Ridables + this.igniteForSeconds(8.0F); + } +@@ -374,6 +379,7 @@ public class Phantom extends FlyingMob 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()); + +@@ -739,6 +745,12 @@ public class Phantom extends FlyingMob 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/0013-Make-entity-breeding-times-configurable.patch b/purpur-server/minecraft-patches/features/0013-Make-entity-breeding-times-configurable.patch new file mode 100644 index 0000000000..be0ec10711 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0013-Make-entity-breeding-times-configurable.patch @@ -0,0 +1,600 @@ +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 4fb63e58eac5d67fcd31c3233dca1dae72b98bc4..dd8d315eba203db121e24e3402f2117fc0f3043f 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 { + return Optional.empty(); + } + // Move age setting down +- 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 + level.broadcastEntityEvent(breedOffspring, (byte)12); +diff --git a/net/minecraft/world/entity/animal/Animal.java b/net/minecraft/world/entity/animal/Animal.java +index 33c3752be451508343cad83766da7c3be1822d02..fa34e7f1c20dfd569b52a9c8e0a8d4d5e659ce20 100644 +--- a/net/minecraft/world/entity/animal/Animal.java ++++ b/net/minecraft/world/entity/animal/Animal.java +@@ -40,6 +40,7 @@ public abstract class Animal extends AgeableMob { + @Nullable + public UUID loveCause; + public ItemStack breedItem; // CraftBukkit - Add breedItem variable ++ public abstract int getPurpurBreedTime(); // Purpur - Make entity breeding times configurable + + protected Animal(EntityType entityType, Level level) { + super(entityType, level); +@@ -279,8 +280,10 @@ public abstract class Animal extends AgeableMob { + player.awardStat(Stats.ANIMALS_BRED); + CriteriaTriggers.BRED_ANIMALS.trigger(player, this, animal, baby); + } // Paper +- 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, (byte)18); +diff --git a/net/minecraft/world/entity/animal/Bee.java b/net/minecraft/world/entity/animal/Bee.java +index c150ba5f706b3dd51925533300c0432ccf5e2b81..6f0b927101f9b5a07a0b6749557f6b0ebf35ae64 100644 +--- a/net/minecraft/world/entity/animal/Bee.java ++++ b/net/minecraft/world/entity/animal/Bee.java +@@ -480,6 +480,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 int getRemainingPersistentAngerTime() { + return this.entityData.get(DATA_REMAINING_ANGER_TIME); +diff --git a/net/minecraft/world/entity/animal/Cat.java b/net/minecraft/world/entity/animal/Cat.java +index 98ce277c5b27591e22daa3c85241be1b8689bfae..584568cef949cee24aa7850d2ff99d47cd089a6e 100644 +--- a/net/minecraft/world/entity/animal/Cat.java ++++ b/net/minecraft/world/entity/animal/Cat.java +@@ -126,6 +126,13 @@ public class Cat extends TamableAnimal implements VariantHolder itemStack.is(ItemTags.CAT_FOOD), true); +diff --git a/net/minecraft/world/entity/animal/Chicken.java b/net/minecraft/world/entity/animal/Chicken.java +index d718f0ed362c49803260dceed64bd93e2b6744fc..39ad1729ef03fc35a6365ee215be214eccfd959a 100644 +--- a/net/minecraft/world/entity/animal/Chicken.java ++++ b/net/minecraft/world/entity/animal/Chicken.java +@@ -81,6 +81,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.java b/net/minecraft/world/entity/animal/Cow.java +index d2a4bfa5334f7361067e4adac36ba5a4a4fa6ad8..e4965300eb41512d03a0b111422c98627cf29a54 100644 +--- a/net/minecraft/world/entity/animal/Cow.java ++++ b/net/minecraft/world/entity/animal/Cow.java +@@ -63,6 +63,13 @@ public class Cow extends Animal { + } + // 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 registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/Fox.java b/net/minecraft/world/entity/animal/Fox.java +index 8bf893837586ae2a9b4ef7564d242e16e4863b5d..1acf9b8c9e6a5915b3f095e83d3f209708947093 100644 +--- a/net/minecraft/world/entity/animal/Fox.java ++++ b/net/minecraft/world/entity/animal/Fox.java +@@ -175,6 +175,13 @@ public class Fox extends Animal implements VariantHolder { + } + // 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); +@@ -969,8 +976,10 @@ public class Fox extends Animal implements VariantHolder { + 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(); + serverLevel.addFreshEntityWithPassengers(fox, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason +diff --git a/net/minecraft/world/entity/animal/MushroomCow.java b/net/minecraft/world/entity/animal/MushroomCow.java +index 990723c31aa1040a4e45b9857a18d86287ef91b4..a64b609bf5ce38a252bfa1bcff869f88e14389b5 100644 +--- a/net/minecraft/world/entity/animal/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/MushroomCow.java +@@ -79,6 +79,13 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { + } + // 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.java b/net/minecraft/world/entity/animal/Sheep.java +index b66440f5cfbd714c6d2f5b7f66b4e755602b4521..882c799cb66a2acada33ff24f3adb7eb611f89c1 100644 +--- a/net/minecraft/world/entity/animal/Sheep.java ++++ b/net/minecraft/world/entity/animal/Sheep.java +@@ -106,6 +106,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/Turtle.java b/net/minecraft/world/entity/animal/Turtle.java +index 4f0fbbb2caeda6d1477d3297fd68f802e4f3a9ca..edbccb7ca27aa8a1917eb8b35b3ba8600c91111a 100644 +--- a/net/minecraft/world/entity/animal/Turtle.java ++++ b/net/minecraft/world/entity/animal/Turtle.java +@@ -109,6 +109,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.entityData.set(HOME_POS, homePos); + } +diff --git a/net/minecraft/world/entity/animal/Wolf.java b/net/minecraft/world/entity/animal/Wolf.java +index 93a0fda3a2f3598c9f5606e4f379b60a2129a07b..136f2c43272e5a45e473b66656818ed88de1cff3 100644 +--- a/net/minecraft/world/entity/animal/Wolf.java ++++ b/net/minecraft/world/entity/animal/Wolf.java +@@ -210,6 +210,13 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder, B + } + // 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/camel/Camel.java b/net/minecraft/world/entity/animal/camel/Camel.java +index 1d7e2358bac193af48dc4b7f5b0295e3bffa152b..1d7ae2a08968860636918e7c66b60139a9d761b4 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 { + } + // 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 + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); +diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java +index 9a400c8bf2b54aa5fbcbe65b61670cac5fbebf05..c4ea9485294b7dec2582c638802f003ad70659b6 100644 +--- a/net/minecraft/world/entity/animal/frog/Frog.java ++++ b/net/minecraft/world/entity/animal/frog/Frog.java +@@ -165,6 +165,12 @@ public class Frog extends Animal implements VariantHolder> { + } + // 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 9924a39953fb49954d02c771ae1a51411226ceac..16fe9367466372eb7cd0ecf24ba5b7cbc64a820c 100644 +--- a/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/net/minecraft/world/entity/animal/goat/Goat.java +@@ -128,6 +128,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/horse/Donkey.java b/net/minecraft/world/entity/animal/horse/Donkey.java +index 223f1d109680e3643ab2c8343be22713e89755fd..b977597785df5665176ab2f330633ec61b7c9feb 100644 +--- a/net/minecraft/world/entity/animal/horse/Donkey.java ++++ b/net/minecraft/world/entity/animal/horse/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 + protected SoundEvent getAmbientSound() { + return SoundEvents.DONKEY_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/horse/Horse.java b/net/minecraft/world/entity/animal/horse/Horse.java +index 8bd118e82da9e4d4153de0a3efaf6d69e3c4c540..0339ab08b3029a9ffc102c5b865e411aca2a863c 100644 +--- a/net/minecraft/world/entity/animal/horse/Horse.java ++++ b/net/minecraft/world/entity/animal/horse/Horse.java +@@ -67,6 +67,13 @@ public class Horse extends AbstractHorse implements VariantHolder { + } + // 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/horse/Llama.java b/net/minecraft/world/entity/animal/horse/Llama.java +index 58e726dd33f572a31b4910b9ff666c4252fb03a9..6efe52edb6909ed2b38210ce6a0334eddc55f261 100644 +--- a/net/minecraft/world/entity/animal/horse/Llama.java ++++ b/net/minecraft/world/entity/animal/horse/Llama.java +@@ -141,6 +141,13 @@ public class Llama extends AbstractChestedHorse implements VariantHolder 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 54924cd7c84cbcd22ffc0bd37fc24f24e73c18bc..266d1838e6602ef6322c15732f2693a865911f2e 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 { + this.timeInOverworld = timeInOverworld; + } + ++ // Purpur start - Make entity breeding times configurable ++ @Override ++ public int getPurpurBreedTime() { ++ return this.level().purpurConfig.hoglinBreedingTicks; ++ } ++ // Purpur end - Make entity breeding times configurable ++ + @Override + public boolean canBeLeashed() { + return true; diff --git a/purpur-server/minecraft-patches/features/0014-Apply-display-names-from-item-forms-of-entities-to-e.patch b/purpur-server/minecraft-patches/features/0014-Apply-display-names-from-item-forms-of-entities-to-e.patch new file mode 100644 index 0000000000..15d2447244 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0014-Apply-display-names-from-item-forms-of-entities-to-e.patch @@ -0,0 +1,111 @@ +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 d368b1971b270c44efc849464a100832bc29a679..8c0ab32487c56e2caf42404184f86c9bcf5f8b41 100644 +--- a/net/minecraft/world/entity/decoration/ArmorStand.java ++++ b/net/minecraft/world/entity/decoration/ArmorStand.java +@@ -564,6 +564,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 65e1d7c5ac94b1cfb921fa009be59d3e5872f0b5..3ee1d8798db666ee8d83556047e40ff217cda732 100644 +--- a/net/minecraft/world/entity/decoration/ItemFrame.java ++++ b/net/minecraft/world/entity/decoration/ItemFrame.java +@@ -223,7 +223,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.java b/net/minecraft/world/entity/decoration/Painting.java +index 5b905a4d49c44b04d5795c2bf297f3c69d183d7c..b6429a2bbb6fc1e08610ab20e50f8f0414f0ad26 100644 +--- a/net/minecraft/world/entity/decoration/Painting.java ++++ b/net/minecraft/world/entity/decoration/Painting.java +@@ -162,7 +162,11 @@ public class Painting extends HangingEntity implements VariantHolder +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 4106549bd4dec1cc47d8765be8f5d119fe33bf56..e98fac58b29f78cb63bd868811cca41e1644e9ac 100644 +--- a/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java ++++ b/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java +@@ -32,6 +32,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( +@@ -50,9 +51,10 @@ public class HarvestFarmland extends Behavior { + protected boolean checkExtraStartConditions(ServerLevel level, Villager owner) { + if (!level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { + return false; +- } else if (owner.getVillagerData().getProfession() != VillagerProfession.FARMER) { ++ } else if (owner.getVillagerData().getProfession() != VillagerProfession.FARMER && !(level.purpurConfig.villagerClericsFarmWarts && owner.getVillagerData().getProfession() == VillagerProfession.CLERIC)) { // Purpur - Option for Villager Clerics to farm Nether Wart + return false; + } else { ++ if (!this.clericWartFarmer && owner.getVillagerData().getProfession() == VillagerProfession.CLERIC) this.clericWartFarmer = true; // Purpur - Option for Villager Clerics to farm Nether Wart + BlockPos.MutableBlockPos mutableBlockPos = owner.blockPosition().mutable(); + this.validFarmlandAroundVillager.clear(); + +@@ -83,6 +85,7 @@ public class HarvestFarmland extends Behavior { + BlockState blockState = serverLevel.getBlockState(pos); + Block block = blockState.getBlock(); + Block block1 = serverLevel.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; + } + +@@ -109,19 +112,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); +@@ -136,7 +139,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 243ac036f95794e7766aefb4630b635681ae5a5f..4d8523a43d60cd6b4fd5546ffb3a61417b2c475b 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().getProfession() == 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 84afd8646b05409c582f29d73f9fea4b09feb603..32779b121322688a4b14e460b1f902ef67770b32 100644 +--- a/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java ++++ b/net/minecraft/world/entity/ai/behavior/VillagerGoalPackages.java +@@ -74,8 +74,13 @@ public class VillagerGoalPackages { + } + + public static ImmutableList>> getWorkPackage(VillagerProfession profession, float speedModifier) { ++ // Purpur start - Option for Villager Clerics to farm Nether Wart ++ return getWorkPackage(profession, speedModifier, false); ++ } ++ public static ImmutableList>> getWorkPackage(VillagerProfession profession, float speedModifier, boolean clericsFarmWarts) { ++ // Purpur end - Option for Villager Clerics to farm Nether Wart + WorkAtPoi workAtPoi; +- if (profession == VillagerProfession.FARMER) { ++ if (profession == VillagerProfession.FARMER || (clericsFarmWarts && profession == 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 6b99afb4f237b5d6def98f3e03492975b795bc95..234e9d4aca14bc2a2e138918be1430516d710060 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().getProfession() == net.minecraft.world.entity.npc.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.java b/net/minecraft/world/entity/npc/Villager.java +index 2f685a186b2dc27e70cddd5c4951c27e7ee3ef53..c301a89f032746487a4e993d920060450433f238 100644 +--- a/net/minecraft/world/entity/npc/Villager.java ++++ b/net/minecraft/world/entity/npc/Villager.java +@@ -311,7 +311,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + villagerBrain.setSchedule(Schedule.VILLAGER_DEFAULT); + villagerBrain.addActivityWithConditions( + Activity.WORK, +- VillagerGoalPackages.getWorkPackage(profession, 0.5F), ++ VillagerGoalPackages.getWorkPackage(profession, 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)) + ); + } +@@ -976,7 +976,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + + public boolean hasFarmSeeds() { +- return this.getInventory().hasAnyMatching(itemStack -> itemStack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS)); ++ return this.getInventory().hasAnyMatching(itemStack -> this.level().purpurConfig.villagerClericsFarmWarts && this.getVillagerData().getProfession() == VillagerProfession.CLERIC ? itemStack.is(Items.NETHER_WART) : itemStack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS)); // Purpur - Option for Villager Clerics to farm Nether Wart + } + + @Override +diff --git a/net/minecraft/world/entity/npc/VillagerProfession.java b/net/minecraft/world/entity/npc/VillagerProfession.java +index 1ec8ad124c76483d11057eb97fcfb9aebee0c301..f783d058a080408d572938d36ba031f33a08e9af 100644 +--- a/net/minecraft/world/entity/npc/VillagerProfession.java ++++ b/net/minecraft/world/entity/npc/VillagerProfession.java +@@ -31,7 +31,7 @@ public record VillagerProfession( + 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 - Option for Villager Clerics to farm Nether Wart + public static final VillagerProfession FARMER = register( + "farmer", + PoiTypes.FARMER, diff --git a/purpur-server/minecraft-patches/features/0016-Add-mobGriefing-bypass-to-everything-affected.patch b/purpur-server/minecraft-patches/features/0016-Add-mobGriefing-bypass-to-everything-affected.patch new file mode 100644 index 0000000000..4e21dcf5d0 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0016-Add-mobGriefing-bypass-to-everything-affected.patch @@ -0,0 +1,360 @@ +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/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index 6de5f527c018201d874e06a45c9509fa12125766..2dc588e2d503c16ccd2589ce18abd2ecebbc8e74 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -1818,7 +1818,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + if (this.level() instanceof ServerLevel serverLevel) { + boolean var6 = false; + if (this.dead && entitySource instanceof WitherBoss) { // Paper +- if (serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { ++ if (serverLevel.purpurConfig.witherBypassMobGriefing ^ serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass 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 c431f28c3f4f6cec946048f5752c364429af5ba1..d93584c6793818463e8883ffe399bf16b03263a9 100644 +--- a/net/minecraft/world/entity/Mob.java ++++ b/net/minecraft/world/entity/Mob.java +@@ -648,7 +648,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + && this.canPickUpLoot() + && this.isAlive() + && !this.dead +- && serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { ++ && serverLevel.purpurConfig.entitiesPickUpLootBypassMobGriefing ^ serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass 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 e98fac58b29f78cb63bd868811cca41e1644e9ac..56d49bc71cb0cb0a08ff771991fd77ab774b4b59 100644 +--- a/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java ++++ b/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java +@@ -49,7 +49,7 @@ public class HarvestFarmland extends Behavior { + + @Override + protected boolean checkExtraStartConditions(ServerLevel level, Villager owner) { +- if (!level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { ++ if (!level.purpurConfig.villagerBypassMobGriefing == !level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected + return false; + } else if (owner.getVillagerData().getProfession() != VillagerProfession.FARMER && !(level.purpurConfig.villagerClericsFarmWarts && owner.getVillagerData().getProfession() == 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 e026e07ca86700c864a3dceda6817fb7b6cb11e9..f1dfe0bf047e7d331b2379a672ff7b8eae4c9c90 100644 +--- a/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java ++++ b/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java +@@ -30,7 +30,7 @@ public class BreakDoorGoal extends DoorInteractGoal { + @Override + public boolean canUse() { + return super.canUse() +- && getServerLevel(this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ++ && this.mob.level().purpurConfig.zombieBypassMobGriefing == !getServerLevel(this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) // Purpur - Add mobGriefing bypass 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 e84893780b533b1ecb3675606b5c2daf7339b861..65eb4d8001b07cb3f7cda17565eea10a88a9c47c 100644 +--- a/net/minecraft/world/entity/ai/goal/EatBlockGoal.java ++++ b/net/minecraft/world/entity/ai/goal/EatBlockGoal.java +@@ -67,7 +67,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_TALL_GRASS.test(blockState)) { // Paper - fix wrong block state +- if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockPos, blockState.getFluidState().createLegacyBlock(), !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - fix wrong block state ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockPos, blockState.getFluidState().createLegacyBlock(), !getServerLevel(this.level).purpurConfig.sheepBypassMobGriefing == !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - fix wrong block state // Purpur - Add mobGriefing bypass to everything affected + this.level.destroyBlock(blockPos, false); + } + +@@ -75,7 +75,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().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - Fix wrong block state ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockPos1, Blocks.DIRT.defaultBlockState(), !getServerLevel(this.level).purpurConfig.sheepBypassMobGriefing == !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - Fix wrong block state // Purpur - Add mobGriefing bypass to everything affected + this.level.levelEvent(2001, blockPos1, Block.getId(Blocks.GRASS_BLOCK.defaultBlockState())); + this.level.setBlock(blockPos1, Blocks.DIRT.defaultBlockState(), 2); + } +diff --git a/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java b/net/minecraft/world/entity/ai/goal/RemoveBlockGoal.java +index 579ca031d461ed4327fe4fb45c5289565322e64e..95fa516910a3834bbd4db6d11279e13a1f0dac41 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().getBoolean(GameRules.RULE_MOBGRIEFING)) { ++ if (!getServerLevel(this.removerMob).purpurConfig.zombieBypassMobGriefing == !getServerLevel(this.removerMob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected + return false; + } else if (this.nextStartTick > 0) { + this.nextStartTick--; +diff --git a/net/minecraft/world/entity/animal/Fox.java b/net/minecraft/world/entity/animal/Fox.java +index 1acf9b8c9e6a5915b3f095e83d3f209708947093..3d94d5c9ecab0fe7332daf4cdac879385159eaa1 100644 +--- a/net/minecraft/world/entity/animal/Fox.java ++++ b/net/minecraft/world/entity/animal/Fox.java +@@ -1038,7 +1038,7 @@ public class Fox extends Animal implements VariantHolder { + } + + protected void onReachedTarget() { +- if (getServerLevel(Fox.this.level()).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { ++ if (getServerLevel(Fox.this.level()).purpurConfig.foxBypassMobGriefing ^ getServerLevel(Fox.this.level()).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass 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/Rabbit.java b/net/minecraft/world/entity/animal/Rabbit.java +index bbdd06002b07699fffebdf6ed8148abdb69c24cc..7379def14f3f700fb8a746dc89d89e249e93b7b9 100644 +--- a/net/minecraft/world/entity/animal/Rabbit.java ++++ b/net/minecraft/world/entity/animal/Rabbit.java +@@ -620,7 +620,7 @@ public class Rabbit extends Animal implements VariantHolder { + @Override + public boolean canUse() { + if (this.nextStartTick <= 0) { +- if (!getServerLevel(this.rabbit).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { ++ if (!getServerLevel(this.rabbit).purpurConfig.rabbitBypassMobGriefing == !getServerLevel(this.rabbit).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected + return false; + } + +diff --git a/net/minecraft/world/entity/animal/SnowGolem.java b/net/minecraft/world/entity/animal/SnowGolem.java +index d97a297db3bec0c86c6a82ef1c353015df2115f7..6ee73b798ab306f7c828c9f06ca5b1a96bd96139 100644 +--- a/net/minecraft/world/entity/animal/SnowGolem.java ++++ b/net/minecraft/world/entity/animal/SnowGolem.java +@@ -136,7 +136,7 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM + this.hurtServer(serverLevel, this.damageSources().melting(), 1.0F); // CraftBukkit - DamageSources.ON_FIRE -> CraftEventFactory.MELTING + } + +- if (!serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { ++ if (!serverLevel.purpurConfig.snowGolemBypassMobGriefing == !serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected + return; + } + +diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +index ca900bb646e16c7b4342f23c3ffae786eab28145..724d259d4b793f2043e63dda9022bdfddc4dca38 100644 +--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java ++++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +@@ -547,7 +547,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().getBoolean(GameRules.RULE_MOBGRIEFING) && !blockState.is(BlockTags.DRAGON_IMMUNE)) { ++ if (level.purpurConfig.enderDragonBypassMobGriefing ^ level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && !blockState.is(BlockTags.DRAGON_IMMUNE)) { // Purpur - Add mobGriefing bypass 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 60e666aa8afe14b519010b6d137a89e3d22f6c81..a325fa87e149e7f354ed4cf3dbb30a002c4ce32a 100644 +--- a/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -493,7 +493,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { + + if (this.destroyBlocksTick > 0) { + this.destroyBlocksTick--; +- if (this.destroyBlocksTick == 0 && level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { ++ if (this.destroyBlocksTick == 0 && level.purpurConfig.witherBypassMobGriefing ^ level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass 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 f8d6935439b4e672ed655b2a458451d4b1fa8ffd..7b74322aef3d7d45a322abccc71d9168b3c0911b 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -516,7 +516,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().getBoolean(GameRules.RULE_MOBGRIEFING) ++ && getServerLevel(this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) == !this.enderman.level().purpurConfig.endermanBypassMobGriefing // Purpur - Add mobGriefing bypass to everything affected + && this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0; + } + +@@ -666,7 +666,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().getBoolean(GameRules.RULE_MOBGRIEFING) ++ && getServerLevel(this.enderman).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) == !this.enderman.level().purpurConfig.endermanBypassMobGriefing // Purpur - Add mobGriefing bypass to everything affected + && this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0; + } + +diff --git a/net/minecraft/world/entity/monster/Evoker.java b/net/minecraft/world/entity/monster/Evoker.java +index d3d7e11a12af404d83e81888a9a633dfb93412ec..91574baf7ca095eae909e8e7225ad500bde15af2 100644 +--- a/net/minecraft/world/entity/monster/Evoker.java ++++ b/net/minecraft/world/entity/monster/Evoker.java +@@ -323,7 +323,7 @@ public class Evoker extends SpellcasterIllager { + return false; + } else { + ServerLevel serverLevel = getServerLevel(Evoker.this.level()); +- if (!serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { ++ if (!serverLevel.purpurConfig.evokerBypassMobGriefing == !serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass to everything affected + return false; + } else { + List nearbyEntities = serverLevel.getNearbyEntities( +diff --git a/net/minecraft/world/entity/monster/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java +index 36ebfc1102a18e4050eb9a2441d75bafcf3784b8..3449628fb87fd760abd730d84699c3a09c6ec761 100644 +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -175,7 +175,7 @@ public class Ravager extends Raider { + + if (this.level() instanceof ServerLevel serverLevel + && this.horizontalCollision +- && serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { ++ && serverLevel.purpurConfig.ravagerBypassMobGriefing ^ serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { // Purpur - Add mobGriefing bypass 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 d3befe91bc65bbc2bc0d8651b78e8c9576cd0f75..0d3b8b64a23a19d67a1a4a01faaf6649a59f54ad 100644 +--- a/net/minecraft/world/entity/monster/Silverfish.java ++++ b/net/minecraft/world/entity/monster/Silverfish.java +@@ -170,7 +170,7 @@ public class Silverfish extends Monster { + return false; + } else { + RandomSource random = this.mob.getRandom(); +- if (getServerLevel(this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && random.nextInt(reducedTickDelay(10)) == 0) { ++ if (getServerLevel(this.mob).purpurConfig.silverfishBypassMobGriefing ^ getServerLevel(this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && random.nextInt(reducedTickDelay(10)) == 0) { // Purpur - Add mobGriefing bypass 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); +@@ -247,7 +247,7 @@ public class Silverfish extends Monster { + Block block = blockState.getBlock(); + if (block instanceof InfestedBlock) { + // CraftBukkit start +- BlockState afterState = getServerLevel(level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? blockState.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(level.getBlockState(blockPos1)); // Paper - fix wrong block state ++ BlockState afterState = getServerLevel(level).purpurConfig.silverfishBypassMobGriefing ^ getServerLevel(level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? blockState.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(level.getBlockState(blockPos1)); // Paper - fix wrong block state // Purpur - Add mobGriefing bypass 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/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java +index 897c57263ab7347987b289016a71d11f693bc8b2..d923a424e2b33b7d4e9e4ecdce8e0a8c825038de 100644 +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -473,7 +473,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + + @Override + public boolean wantsToPickUp(ServerLevel level, ItemStack stack) { +- return level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack); ++ return level.purpurConfig.piglinBypassMobGriefing ^ level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack); // Purpur - Add mobGriefing bypass to everything affected + } + + protected boolean canReplaceCurrentItem(ItemStack candidate) { +diff --git a/net/minecraft/world/entity/projectile/LargeFireball.java b/net/minecraft/world/entity/projectile/LargeFireball.java +index 4a752ace041228f095af7b1b4878a03c5ed2381f..3e8b5d042eddb817dee2504ff9aa263f6195b1c7 100644 +--- a/net/minecraft/world/entity/projectile/LargeFireball.java ++++ b/net/minecraft/world/entity/projectile/LargeFireball.java +@@ -18,20 +18,20 @@ public class LargeFireball extends Fireball { + + public LargeFireball(EntityType entityType, Level level) { + super(entityType, level); +- this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit ++ this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.purpurConfig.fireballsBypassMobGriefing ^ serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit // Purpur - Add mobGriefing bypass 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().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit ++ this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.purpurConfig.fireballsBypassMobGriefing ^ serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit // Purpur - Add mobGriefing bypass to everything affected + } + + @Override + protected void onHit(HitResult result) { + super.onHit(result); + if (this.level() instanceof ServerLevel serverLevel) { +- boolean _boolean = serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); ++ boolean _boolean = serverLevel.purpurConfig.fireballsBypassMobGriefing ^ serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur - Add mobGriefing bypass 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()); + this.level().getCraftServer().getPluginManager().callEvent(event); +diff --git a/net/minecraft/world/entity/projectile/Projectile.java b/net/minecraft/world/entity/projectile/Projectile.java +index ad0bb896d6ea669ce88bfe6490319e8ba7a29001..843f1396a6567672e5e8002d7e48fb18cf39d5de 100644 +--- a/net/minecraft/world/entity/projectile/Projectile.java ++++ b/net/minecraft/world/entity/projectile/Projectile.java +@@ -454,7 +454,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().getBoolean(GameRules.RULE_MOBGRIEFING); ++ return owner instanceof Player ? owner.mayInteract(level, pos) : owner == null || level.purpurConfig.projectilesBypassMobGriefing ^ level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur - Add mobGriefing bypass to everything affected + } + + public boolean mayBreak(ServerLevel level) { +diff --git a/net/minecraft/world/entity/projectile/SmallFireball.java b/net/minecraft/world/entity/projectile/SmallFireball.java +index 8c84cea43fc0e42a576004663670977eac99f1a6..808aa5dcb27c87b6ba5c1eee639486067447e370 100644 +--- a/net/minecraft/world/entity/projectile/SmallFireball.java ++++ b/net/minecraft/world/entity/projectile/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().getBoolean(GameRules.RULE_MOBGRIEFING); ++ this.isIncendiary = (level instanceof ServerLevel serverLevel) && serverLevel.purpurConfig.fireballsBypassMobGriefing ^ serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur - Add mobGriefing bypass to everything affected + } + // CraftBukkit end + } +diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java +index 8270d76a753bfd26a4c8ef6610bee5c24ee59cfe..c06b589e669b055a26f662df60070d5908256220 100644 +--- a/net/minecraft/world/entity/raid/Raider.java ++++ b/net/minecraft/world/entity/raid/Raider.java +@@ -399,7 +399,7 @@ public abstract class Raider extends PatrollingMonster { + } + + private boolean cannotPickUpBanner() { +- if (!getServerLevel(this.mob).getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items ++ if (!this.mob.level().purpurConfig.pillagerBypassMobGriefing == !getServerLevel(this.mob).getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items // Purpur - Add mobGriefing bypass 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 b43f16297ac4441d78d0dc1fadb555698b0c3f6d..27f0c5c886a3f8b14ef9a00e2aaaabf4bf09c7db 100644 +--- a/net/minecraft/world/level/block/CropBlock.java ++++ b/net/minecraft/world/level/block/CropBlock.java +@@ -182,7 +182,7 @@ public class CropBlock extends BushBlock implements BonemealableBlock { + @Override + protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { + 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().getBoolean(GameRules.RULE_MOBGRIEFING))) { // 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.purpurConfig.ravagerBypassMobGriefing == !serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Purpur - Configurable ravager griefable blocks list // Purpur - Add mobGriefing bypass 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 6ab9a1188ce60e114ef35e393d9f2f46d490af83..9a7ca836e54cc3f58001c85f0079747f4d4941ad 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.5F) // // Purpur - Configurable farmland trample height + && entity instanceof LivingEntity +- && (entity instanceof Player || serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) ++ && (entity instanceof Player || serverLevel.purpurConfig.farmlandBypassMobGriefing ^ serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) + && 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 9c0ded7ae7e3a520704033a866f80743ae85d772..4f3646961beb877520e257e11224c3045467d351 100644 +--- a/net/minecraft/world/level/block/PowderSnowBlock.java ++++ b/net/minecraft/world/level/block/PowderSnowBlock.java +@@ -84,7 +84,7 @@ public class PowderSnowBlock extends Block implements BucketPickup { + // CraftBukkit - move down + && entity.mayInteract(serverLevel, pos)) { + // CraftBukkit start +- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !(serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || entity instanceof Player))) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !(serverLevel.purpurConfig.powderSnowBypassMobGriefing ^ serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || entity instanceof Player))) { + return; + } + // CraftBukkit end +diff --git a/net/minecraft/world/level/block/TurtleEggBlock.java b/net/minecraft/world/level/block/TurtleEggBlock.java +index c97d5bd7b4c1b6f8eccbe2d123292e913a285ef2..a3a093d95306baac22e5cf720f5b14f733b548d4 100644 +--- a/net/minecraft/world/level/block/TurtleEggBlock.java ++++ b/net/minecraft/world/level/block/TurtleEggBlock.java +@@ -216,7 +216,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().getBoolean(GameRules.RULE_MOBGRIEFING); ++ return level.purpurConfig.turtleEggsBypassMobGriefing ^ level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // Purpur - Add mobGriefing bypass to everything affected + // Purpur end - Add turtle egg block options + } + } diff --git a/purpur-server/minecraft-patches/features/0017-Add-EntityTeleportHinderedEvent.patch b/purpur-server/minecraft-patches/features/0017-Add-EntityTeleportHinderedEvent.patch new file mode 100644 index 0000000000..3947801b10 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0017-Add-EntityTeleportHinderedEvent.patch @@ -0,0 +1,72 @@ +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/net/minecraft/world/level/block/EndGatewayBlock.java b/net/minecraft/world/level/block/EndGatewayBlock.java +index 84a1bd5e40e635962d795506861447851e443eee..54abeb142e119edd1c1d1c263821b95b1f05c388 100644 +--- a/net/minecraft/world/level/block/EndGatewayBlock.java ++++ b/net/minecraft/world/level/block/EndGatewayBlock.java +@@ -98,6 +98,13 @@ public class EndGatewayBlock extends BaseEntityBlock implements Portal { + org.bukkit.event.entity.EntityPortalEnterEvent event = new org.bukkit.event.entity.EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(level.getWorld(), pos.getX(), pos.getY(), pos.getZ()), org.bukkit.PortalType.END_GATEWAY); // Paper - add portal type + if (!event.callEvent()) return; + // Paper end - call EntityPortalEnterEvent ++ // Purpur start - Add EntityTeleportHinderedEvent ++ if (level.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, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_GATEWAY).callEvent()) { ++ return; ++ } ++ } ++ // Purpur end - Add EntityTeleportHinderedEvent + entity.setAsInsidePortal(this, pos); + TheEndGatewayBlockEntity.triggerCooldown(level, pos, state, theEndGatewayBlockEntity); + } +diff --git a/net/minecraft/world/level/block/EndPortalBlock.java b/net/minecraft/world/level/block/EndPortalBlock.java +index 01cddd7001b4a7f99c1b1d147fac904d3064d733..7e60bcadd2d343e00fc554dba0b85c9191f7efb6 100644 +--- a/net/minecraft/world/level/block/EndPortalBlock.java ++++ b/net/minecraft/world/level/block/EndPortalBlock.java +@@ -58,6 +58,13 @@ public class EndPortalBlock extends BaseEntityBlock implements Portal { + protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (entity.canUsePortal(false)) { ++ // Purpur start - Add EntityTeleportHinderedEvent ++ if (level.purpurConfig.imposeTeleportRestrictionsOnEndPortals && (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, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.END_PORTAL).callEvent()) { ++ return; ++ } ++ } ++ // Purpur end - Add EntityTeleportHinderedEvent + // CraftBukkit start - Entity in portal + org.bukkit.event.entity.EntityPortalEnterEvent event = new org.bukkit.event.entity.EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(level.getWorld(), pos.getX(), pos.getY(), pos.getZ()), org.bukkit.PortalType.ENDER); // Paper - add portal type + level.getCraftServer().getPluginManager().callEvent(event); +diff --git a/net/minecraft/world/level/block/NetherPortalBlock.java b/net/minecraft/world/level/block/NetherPortalBlock.java +index 006444811c00a8820bddaf75950e794271efdd82..eb659209008209c0930770e5f9671a3d7a4abae6 100644 +--- a/net/minecraft/world/level/block/NetherPortalBlock.java ++++ b/net/minecraft/world/level/block/NetherPortalBlock.java +@@ -117,6 +117,13 @@ public class NetherPortalBlock extends Block implements Portal { + protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (entity.canUsePortal(false)) { ++ // Purpur start - Add EntityTeleportHinderedEvent ++ if (level.purpurConfig.imposeTeleportRestrictionsOnNetherPortals && (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, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.NETHER_PORTAL).callEvent()) { ++ return; ++ } ++ } ++ // Purpur end - Add EntityTeleportHinderedEvent + // CraftBukkit start - Entity in portal + org.bukkit.event.entity.EntityPortalEnterEvent event = new org.bukkit.event.entity.EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(level.getWorld(), pos.getX(), pos.getY(), pos.getZ()), org.bukkit.PortalType.NETHER); // Paper - add portal type + level.getCraftServer().getPluginManager().callEvent(event); diff --git a/purpur-server/minecraft-patches/features/0018-Toggle-for-water-sensitive-mob-damage.patch b/purpur-server/minecraft-patches/features/0018-Toggle-for-water-sensitive-mob-damage.patch new file mode 100644 index 0000000000..070dfd2e2b --- /dev/null +++ b/purpur-server/minecraft-patches/features/0018-Toggle-for-water-sensitive-mob-damage.patch @@ -0,0 +1,1334 @@ +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/GlowSquid.java b/net/minecraft/world/entity/GlowSquid.java +index 898b1e01026ec1f44cfe60e9f18a997c86e30594..e717c063c8f9623b8c4b4ea3843d05fd79af3653 100644 +--- a/net/minecraft/world/entity/GlowSquid.java ++++ b/net/minecraft/world/entity/GlowSquid.java +@@ -52,6 +52,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/ambient/Bat.java b/net/minecraft/world/entity/ambient/Bat.java +index ecbec552e5cd1935f57872d2fb502d3e9743e3d8..4fa526496265a85b637136f0fd0692ef4f570ad6 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/Bee.java b/net/minecraft/world/entity/animal/Bee.java +index 6f0b927101f9b5a07a0b6749557f6b0ebf35ae64..d4025093d82cca0c5923058dc0e35c91ae7b40e3 100644 +--- a/net/minecraft/world/entity/animal/Bee.java ++++ b/net/minecraft/world/entity/animal/Bee.java +@@ -177,7 +177,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); +- this.setPathfindingMalus(PathType.WATER, -1.0F); ++ if (isSensitiveToWater()) this.setPathfindingMalus(PathType.WATER, -1.0F); // 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); +@@ -487,6 +487,11 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + } + // Purpur end - Make entity breeding times configurable + ++ @Override ++ public boolean isSensitiveToWater() { ++ return this.level().purpurConfig.beeTakeDamageFromWater; ++ } ++ + @Override + public int getRemainingPersistentAngerTime() { + return this.entityData.get(DATA_REMAINING_ANGER_TIME); +diff --git a/net/minecraft/world/entity/animal/Cat.java b/net/minecraft/world/entity/animal/Cat.java +index 584568cef949cee24aa7850d2ff99d47cd089a6e..b41ca04043e65f107edaebc49d398650898e35fb 100644 +--- a/net/minecraft/world/entity/animal/Cat.java ++++ b/net/minecraft/world/entity/animal/Cat.java +@@ -133,6 +133,13 @@ public class Cat extends TamableAnimal implements VariantHolder itemStack.is(ItemTags.CAT_FOOD), true); +diff --git a/net/minecraft/world/entity/animal/Chicken.java b/net/minecraft/world/entity/animal/Chicken.java +index 39ad1729ef03fc35a6365ee215be214eccfd959a..2364596156c21e82879f5bf4fd873b9d90b1c308 100644 +--- a/net/minecraft/world/entity/animal/Chicken.java ++++ b/net/minecraft/world/entity/animal/Chicken.java +@@ -88,6 +88,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/Cod.java b/net/minecraft/world/entity/animal/Cod.java +index 434e1fabf2e360a8f5f4eefed96e3883aa786d10..ac7259cfc8428131f90956d7f76f2227049ffae3 100644 +--- a/net/minecraft/world/entity/animal/Cod.java ++++ b/net/minecraft/world/entity/animal/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/Cow.java b/net/minecraft/world/entity/animal/Cow.java +index e4965300eb41512d03a0b111422c98627cf29a54..a8c76fcbbaa4afd2d0bd568874995b91d8d67c03 100644 +--- a/net/minecraft/world/entity/animal/Cow.java ++++ b/net/minecraft/world/entity/animal/Cow.java +@@ -70,6 +70,13 @@ public class Cow 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.cowTakeDamageFromWater; ++ } ++ // 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/Dolphin.java b/net/minecraft/world/entity/animal/Dolphin.java +index 5b764c686e8759a7b04a7b50708c69629be02c04..e104058b3c01bea4cc8a77de2ad4378465903b34 100644 +--- a/net/minecraft/world/entity/animal/Dolphin.java ++++ b/net/minecraft/world/entity/animal/Dolphin.java +@@ -156,6 +156,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 ++ + @Nullable + @Override + public SpawnGroupData finalizeSpawn( +diff --git a/net/minecraft/world/entity/animal/Fox.java b/net/minecraft/world/entity/animal/Fox.java +index 3d94d5c9ecab0fe7332daf4cdac879385159eaa1..22a70c6af965114e272bb56cb217f975a3cd1bd4 100644 +--- a/net/minecraft/world/entity/animal/Fox.java ++++ b/net/minecraft/world/entity/animal/Fox.java +@@ -182,6 +182,13 @@ public class Fox extends Animal implements VariantHolder { + } + // 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/IronGolem.java b/net/minecraft/world/entity/animal/IronGolem.java +index 37a353cbb0e9b16e0fc92bd1bc8194cb4cd3c13a..23108729ce65ef8b7b215b82f29347513cfd4ebf 100644 +--- a/net/minecraft/world/entity/animal/IronGolem.java ++++ b/net/minecraft/world/entity/animal/IronGolem.java +@@ -98,6 +98,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/MushroomCow.java b/net/minecraft/world/entity/animal/MushroomCow.java +index a64b609bf5ce38a252bfa1bcff869f88e14389b5..5e9795f447e88a42909730d383eaa36acfaf18f5 100644 +--- a/net/minecraft/world/entity/animal/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/MushroomCow.java +@@ -86,6 +86,13 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { + } + // 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/Salmon.java b/net/minecraft/world/entity/animal/Salmon.java +index 93eb3cc3605f694337c1604e2db63fed04693617..8bd4b2b29438bff65ed00a42bbc9639111af181f 100644 +--- a/net/minecraft/world/entity/animal/Salmon.java ++++ b/net/minecraft/world/entity/animal/Salmon.java +@@ -54,6 +54,13 @@ public class Salmon extends AbstractSchoolingFish implements VariantHolder, B + } + // 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/goat/Goat.java b/net/minecraft/world/entity/animal/goat/Goat.java +index 16fe9367466372eb7cd0ecf24ba5b7cbc64a820c..b95570519301bb9e260ce3c20f3231c79ff22fac 100644 +--- a/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/net/minecraft/world/entity/animal/goat/Goat.java +@@ -135,6 +135,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/horse/Donkey.java b/net/minecraft/world/entity/animal/horse/Donkey.java +index b977597785df5665176ab2f330633ec61b7c9feb..1db6ccdc6c83c704aa84a46ee2751a17125bf457 100644 +--- a/net/minecraft/world/entity/animal/horse/Donkey.java ++++ b/net/minecraft/world/entity/animal/horse/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 + protected SoundEvent getAmbientSound() { + return SoundEvents.DONKEY_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/horse/Horse.java b/net/minecraft/world/entity/animal/horse/Horse.java +index 0339ab08b3029a9ffc102c5b865e411aca2a863c..f257d549570918381925cef98734fc0aa605f8f2 100644 +--- a/net/minecraft/world/entity/animal/horse/Horse.java ++++ b/net/minecraft/world/entity/animal/horse/Horse.java +@@ -74,6 +74,13 @@ public class Horse extends AbstractHorse implements VariantHolder { + } + // 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/horse/Llama.java b/net/minecraft/world/entity/animal/horse/Llama.java +index 6efe52edb6909ed2b38210ce6a0334eddc55f261..872a54186a20fd855fe7981f3ff1c867f4c64d19 100644 +--- a/net/minecraft/world/entity/animal/horse/Llama.java ++++ b/net/minecraft/world/entity/animal/horse/Llama.java +@@ -148,6 +148,13 @@ public class Llama extends AbstractChestedHorse implements VariantHolder entityType, Level level) { + super(entityType, 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); +@@ -157,7 +157,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/CaveSpider.java b/net/minecraft/world/entity/monster/CaveSpider.java +index 64eecd8d1acd318743800c1daa77cd97097a0f7c..420fe0d01d0b173a6a541f77e1aaca3f3bb565e7 100644 +--- a/net/minecraft/world/entity/monster/CaveSpider.java ++++ b/net/minecraft/world/entity/monster/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 source) { + if (super.doHurtTarget(level, source)) { +diff --git a/net/minecraft/world/entity/monster/Creeper.java b/net/minecraft/world/entity/monster/Creeper.java +index 31ede62a19321970d26f302399a60947b8d88b37..6d9995d5f38b2f94a2d3362da7861b3e90e4fb6f 100644 +--- a/net/minecraft/world/entity/monster/Creeper.java ++++ b/net/minecraft/world/entity/monster/Creeper.java +@@ -267,6 +267,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 + protected SoundEvent getHurtSound(DamageSource damageSource) { + return SoundEvents.CREEPER_HURT; +diff --git a/net/minecraft/world/entity/monster/Drowned.java b/net/minecraft/world/entity/monster/Drowned.java +index 83fc978a94be4655e8c47ee634b8cd280d2a6fde..0a1a78e65b6472b713835262b5937bc7ea67b95c 100644 +--- a/net/minecraft/world/entity/monster/Drowned.java ++++ b/net/minecraft/world/entity/monster/Drowned.java +@@ -122,6 +122,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/ElderGuardian.java b/net/minecraft/world/entity/monster/ElderGuardian.java +index 148ae4bca77874545a2a05fb7f29f9ac284feff6..2b1d33f4938b978c5b04ede7562bdecb5fbd2245 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 7b74322aef3d7d45a322abccc71d9168b3c0911b..6fccfb243c26c27b665df57e5e19eb3350c52ddf 100644 +--- a/net/minecraft/world/entity/monster/EnderMan.java ++++ b/net/minecraft/world/entity/monster/EnderMan.java +@@ -87,7 +87,7 @@ public class EnderMan extends Monster implements NeutralMob { + + public EnderMan(EntityType entityType, Level level) { + super(entityType, 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 +@@ -294,7 +294,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 2ea45eba13d0b0ea2d3c1d1a3666d6e2e027a3ef..38e3b3ed2c003fe7bbb476f7bf9a882ea2de792a 100644 +--- a/net/minecraft/world/entity/monster/Endermite.java ++++ b/net/minecraft/world/entity/monster/Endermite.java +@@ -70,6 +70,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/Evoker.java b/net/minecraft/world/entity/monster/Evoker.java +index 91574baf7ca095eae909e8e7225ad500bde15af2..61ed6606948cc5f8af543eb9ae05f9aeb4e73b89 100644 +--- a/net/minecraft/world/entity/monster/Evoker.java ++++ b/net/minecraft/world/entity/monster/Evoker.java +@@ -75,6 +75,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/Ghast.java b/net/minecraft/world/entity/monster/Ghast.java +index c5987f50b343ded580b3d3f264498d3893433f92..68debfa8c6d0fc3ff536d2b4d89c131fd6aca935 100644 +--- a/net/minecraft/world/entity/monster/Ghast.java ++++ b/net/minecraft/world/entity/monster/Ghast.java +@@ -84,6 +84,13 @@ public class Ghast extends FlyingMob 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 73da18c4b54e250c434fd75971ef0a8f7c8cf6a3..009b8a8b1b9aabc0aef1c12a0c0f65f9ccfcd306 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).add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.ATTACK_DAMAGE, 50.0); + } +diff --git a/net/minecraft/world/entity/monster/Guardian.java b/net/minecraft/world/entity/monster/Guardian.java +index 546a4fe6d038d04c0be500e76ff4aebb02c3681a..819679a224ffe33e03d8e6b429c8e31b67c769fa 100644 +--- a/net/minecraft/world/entity/monster/Guardian.java ++++ b/net/minecraft/world/entity/monster/Guardian.java +@@ -98,6 +98,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/Husk.java b/net/minecraft/world/entity/monster/Husk.java +index 7a8951f93e65c6df145e30d44b9d928dd0c39189..31eef2869945d9de565d627cac3fc1a5db380a2a 100644 +--- a/net/minecraft/world/entity/monster/Husk.java ++++ b/net/minecraft/world/entity/monster/Husk.java +@@ -67,6 +67,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 ++ + public static boolean checkHuskSpawnRules( + EntityType entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/Illusioner.java b/net/minecraft/world/entity/monster/Illusioner.java +index 1d1cf8748e3fba2e2963ad2fa153fbfe990f5087..ad661f2bf8957644605b52a469d6a7cf8e064398 100644 +--- a/net/minecraft/world/entity/monster/Illusioner.java ++++ b/net/minecraft/world/entity/monster/Illusioner.java +@@ -84,6 +84,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/MagmaCube.java b/net/minecraft/world/entity/monster/MagmaCube.java +index 2c6b0fd46d9ed6a8d1ca7e90ebf596dd3f310f0e..bf26f5f6017c60d5991d5f6c87da2acbd95ef5bb 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 4cc1c8d8967b1e9ee5b0b1c50d887f3de3e8a882..32b7c34d3c68dcfa936b628b2d038524204129a3 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -139,6 +139,13 @@ public class Phantom extends FlyingMob 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/Pillager.java b/net/minecraft/world/entity/monster/Pillager.java +index 9586aa3f3eb61fb0c1224df9d0104da69d7fa6bb..869a1007de13f3f5d757968d0f84cbf43786c870 100644 +--- a/net/minecraft/world/entity/monster/Pillager.java ++++ b/net/minecraft/world/entity/monster/Pillager.java +@@ -88,6 +88,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/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java +index b83886f2533026550759c823e1e11930665fc5bd..2844846811398350832a0f88a72772831e3e43d5 100644 +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -97,6 +97,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 03db684c122a07176aa1365550da935cdb66a1b9..c26e4858a14571d58e439cabd5f2593da4ee2634 100644 +--- a/net/minecraft/world/entity/monster/Shulker.java ++++ b/net/minecraft/world/entity/monster/Shulker.java +@@ -129,6 +129,13 @@ public class Shulker extends AbstractGolem implements VariantHolder entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index f36e94437b4e21961532ac9ab91767617f9c3c32..d01bc46d77340b10b23d0c0d50bddc37657028c8 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -88,7 +88,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { + public Strider(EntityType entityType, Level level) { + super(entityType, 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); +@@ -403,7 +403,7 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { + + @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 8356906b2c0707e21021bb05f9ca01a95682880a..ffdb200d9716104f8df91dbeef590b2264e587b8 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/Vindicator.java b/net/minecraft/world/entity/monster/Vindicator.java +index 0fc1b458101ba9d98d25c9637337caf0949bb893..5f68d73460adfac2ead57d168156a2784af979ae 100644 +--- a/net/minecraft/world/entity/monster/Vindicator.java ++++ b/net/minecraft/world/entity/monster/Vindicator.java +@@ -80,6 +80,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/Witch.java b/net/minecraft/world/entity/monster/Witch.java +index ff8380246f6c6c805b222a91ac6a1eb0d130558d..96ba35f3530ab405a960c79955699666deb6b845 100644 +--- a/net/minecraft/world/entity/monster/Witch.java ++++ b/net/minecraft/world/entity/monster/Witch.java +@@ -81,6 +81,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/WitherSkeleton.java b/net/minecraft/world/entity/monster/WitherSkeleton.java +index 3342f2d92830049837636ff10b5e52f0d85fbd2c..a4dc9b7fbde19b08eb389dc42df21aa5df94e703 100644 +--- a/net/minecraft/world/entity/monster/WitherSkeleton.java ++++ b/net/minecraft/world/entity/monster/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/Zoglin.java b/net/minecraft/world/entity/monster/Zoglin.java +index 132b38d717ac3c5acc64a5ec519f345ac57021d8..79a4a3f4e10e1f9c1a6100060a95636075fc8236 100644 +--- a/net/minecraft/world/entity/monster/Zoglin.java ++++ b/net/minecraft/world/entity/monster/Zoglin.java +@@ -110,6 +110,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/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java +index 98732a5014a1c8a91dbe79d070ce0b58daf1ba1c..5d12bc139c81ca342074c7c745635669020d0300 100644 +--- a/net/minecraft/world/entity/monster/Zombie.java ++++ b/net/minecraft/world/entity/monster/Zombie.java +@@ -139,6 +139,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/ZombieVillager.java b/net/minecraft/world/entity/monster/ZombieVillager.java +index f1e9bf75c50f353bd377051be82a391f97d952fd..94b9abc765b78a40a7ecbf4cbd775b778d49c815 100644 +--- a/net/minecraft/world/entity/monster/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/ZombieVillager.java +@@ -124,6 +124,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/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +index 9603589e0501feee900cd21b04eb84b02bb45de2..09c991d8e344f11bc84dea453042ee35c39e580e 100644 +--- a/net/minecraft/world/entity/monster/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +@@ -105,6 +105,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 + public void setPersistentAngerTarget(@Nullable UUID target) { + this.persistentAngerTarget = target; +diff --git a/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index 266d1838e6602ef6322c15732f2693a865911f2e..896bd531a5333d6dc8996bbdfc5c878b1a7d2da0 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 ++ + @Override + public boolean canBeLeashed() { + return true; +diff --git a/net/minecraft/world/entity/monster/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java +index d923a424e2b33b7d4e9e4ecdce8e0a8c825038de..55ebfa5df06a59203248514d10dced9660ebf215 100644 +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -176,6 +176,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 + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); +diff --git a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +index eb82252cd87797927e153974b9280b5eaa251080..2237681f298113bda0556699e19e880f4b04a853 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/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java +index c301a89f032746487a4e993d920060450433f238..af6ae76248a9894efbecf9e94160f8d215f6ad85 100644 +--- a/net/minecraft/world/entity/npc/Villager.java ++++ b/net/minecraft/world/entity/npc/Villager.java +@@ -278,6 +278,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.java b/net/minecraft/world/entity/npc/WanderingTrader.java +index f9755f36f7863b9742fe5b840a8130891ddff7c7..5a87c3c7aca38f3fe4a003b2075f43b0ae1cddea 100644 +--- a/net/minecraft/world/entity/npc/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/WanderingTrader.java +@@ -107,6 +107,13 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill + } + // 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/0019-API-for-any-mob-to-burn-daylight.patch b/purpur-server/minecraft-patches/features/0019-API-for-any-mob-to-burn-daylight.patch new file mode 100644 index 0000000000..7f3efdce04 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0019-API-for-any-mob-to-burn-daylight.patch @@ -0,0 +1,344 @@ +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 e4ee886f33ee63ae740cd89067c8ec81c12f2a62..f61c29abf75443b206f37b90950d6c0e14ea59ff 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -534,6 +534,24 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + // Purpur end - Add canSaveToDisk to Entity + ++ // Purpur start - copied from Mob - API for any mob to burn daylight ++ public boolean isSunBurnTick() { ++ if (this.level().isDay() && !this.level().isClientSide) { ++ float lightLevelDependentMagicValue = this.getLightLevelDependentMagicValue(); ++ BlockPos blockPos = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); ++ boolean flag = this.isInWaterRainOrBubble() || 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 entityType, Level level) { + this.type = entityType; + this.level = level; +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index 2dc588e2d503c16ccd2589ce18abd2ecebbc8e74..db5a2227009bc4d655fc781d5850221f36f2d112 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -301,6 +301,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 - Friction API ++ 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 + + @Override + public float getBukkitYaw() { +@@ -809,6 +810,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + }); + DataResult dataResult = this.brain.serializeStart(NbtOps.INSTANCE); + dataResult.resultOrPartial(LOGGER::error).ifPresent(brain -> compound.put("Brain", brain)); ++ compound.putBoolean("Purpur.ShouldBurnInDay", this.shouldBurnInDay); // Purpur - API for any mob to burn daylight + } + + @Override +@@ -892,6 +894,12 @@ public abstract class LivingEntity extends Entity implements Attackable { + if (compound.contains("Brain", 10)) { + this.brain = this.makeBrain(new Dynamic<>(NbtOps.INSTANCE, compound.get("Brain"))); + } ++ ++ // Purpur start - API for any mob to burn daylight ++ if (compound.contains("Purpur.ShouldBurnInDay")) { ++ this.shouldBurnInDay = compound.getBoolean("Purpur.ShouldBurnInDay"); ++ } ++ // Purpur end - API for any mob to burn daylight + } + + // CraftBukkit start +@@ -3574,6 +3582,32 @@ public abstract class LivingEntity extends Entity implements Attackable { + if (this.level() instanceof ServerLevel serverLevel && this.isSensitiveToWater() && this.isInWaterRainOrBubble()) { + this.hurtServer(serverLevel, this.damageSources().drown(), 1.0F); + } ++ ++ // Purpur start - copied from Zombie - API for any mob to burn daylight ++ if (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) { ++ ItemStack itemBySlot = this.getItemBySlot(EquipmentSlot.HEAD); ++ 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.HEAD); ++ this.setItemSlot(EquipmentSlot.HEAD, 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); ++ } ++ } ++ } ++ // Purpur end - copied from Zombie - API for any mob to burn daylight + } + + public boolean isSensitiveToWater() { +diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java +index d93584c6793818463e8883ffe399bf16b03263a9..70ee86993d381445855ac7e7290da384d6675987 100644 +--- a/net/minecraft/world/entity/Mob.java ++++ b/net/minecraft/world/entity/Mob.java +@@ -1655,19 +1655,8 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + } + + public boolean isSunBurnTick() { +- if (this.level().isDay() && !this.level().isClientSide) { +- float lightLevelDependentMagicValue = this.getLightLevelDependentMagicValue(); +- BlockPos blockPos = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); +- boolean flag = this.isInWaterRainOrBubble() || 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(); + } + + @Override +diff --git a/net/minecraft/world/entity/monster/AbstractSkeleton.java b/net/minecraft/world/entity/monster/AbstractSkeleton.java +index e186aee80b052b7fc4bfe02763010bfb2d55ea35..223739818e9ac6c9fe396b82bce53a3ab029610a 100644 +--- a/net/minecraft/world/entity/monster/AbstractSkeleton.java ++++ b/net/minecraft/world/entity/monster/AbstractSkeleton.java +@@ -64,11 +64,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 entityType, Level level) { + super(entityType, level); + this.reassessWeaponGoal(); ++ this.setShouldBurnInDay(true); // Purpur - API for any mob to burn daylight + } + + @Override +@@ -110,27 +111,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo + + @Override + public void aiStep() { +- boolean isSunBurnTick = this.shouldBurnInDay && this.isSunBurnTick(); // Paper - shouldBurnInDay API +- if (isSunBurnTick) { +- ItemStack itemBySlot = this.getItemBySlot(EquipmentSlot.HEAD); +- 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.HEAD); +- this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY); +- } +- } +- +- isSunBurnTick = false; +- } +- +- if (isSunBurnTick) { +- this.igniteForSeconds(8.0F); +- } +- } +- ++ // Purpur - implemented in LivingEntity - API for any mob to burn daylight + super.aiStep(); + } + +@@ -243,7 +224,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo + super.readAdditionalSaveData(compound); + this.reassessWeaponGoal(); + // Paper start - shouldBurnInDay API +- if (compound.contains("Paper.ShouldBurnInDay")) { ++ if (false && compound.contains("Paper.ShouldBurnInDay")) { // Purpur - implemented in LivingEntity - API for any mob to burn daylight + this.shouldBurnInDay = compound.getBoolean("Paper.ShouldBurnInDay"); + } + // Paper end - shouldBurnInDay API +@@ -252,7 +233,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo + @Override + public void addAdditionalSaveData(final net.minecraft.nbt.CompoundTag nbt) { + super.addAdditionalSaveData(nbt); +- nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); ++ //nbt.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/Husk.java b/net/minecraft/world/entity/monster/Husk.java +index 31eef2869945d9de565d627cac3fc1a5db380a2a..e618e716cb5ff3a3c5d284e985455694cc0edde0 100644 +--- a/net/minecraft/world/entity/monster/Husk.java ++++ b/net/minecraft/world/entity/monster/Husk.java +@@ -19,6 +19,7 @@ import net.minecraft.world.level.ServerLevelAccessor; + public class Husk extends Zombie { + public Husk(EntityType entityType, Level level) { + super(entityType, level); ++ this.setShouldBurnInDay(false); // Purpur - API for any mob to burn daylight + } + + // Purpur start - Ridables +@@ -82,7 +83,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/Phantom.java b/net/minecraft/world/entity/monster/Phantom.java +index 32b7c34d3c68dcfa936b628b2d038524204129a3..0ee817699fffbb929011465029182cc56befc30c 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/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 - API for any mob to burn daylight + } + + // Purpur start - Ridables +@@ -146,6 +147,16 @@ public class Phantom extends FlyingMob implements Enemy { + } + // Purpur end - Toggle for water sensitive mob damage + ++ //private boolean shouldBurnInDay = true; // Purpur - moved to LivingEntity; keep methods for ABI compatibility - API for any mob to burn daylight ++ // Purpur start - API for any mob to burn daylight ++ 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; ++ } ++ public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } ++ // Purpur end - API for any mob to burn daylight ++ + @Override + public boolean isFlapping() { + return (this.getUniqueFlapTickOffset() + this.tickCount) % TICKS_PER_FLAP == 0; +@@ -261,6 +272,7 @@ public class Phantom extends FlyingMob implements Enemy { + + @Override + public void aiStep() { ++ // Purpur - implemented in LivingEntity; moved down to shouldBurnInDay() - API for any mob to burn daylight + // Purpur start - Phantoms burn in light + boolean burnFromDaylight = this.shouldBurnInDay && this.isSunBurnTick() && this.level().purpurConfig.phantomBurnInDaylight; + boolean burnFromLightSource = this.level().purpurConfig.phantomBurnInLight > 0 && this.level().getMaxLocalRawBrightness(blockPosition()) >= this.level().purpurConfig.phantomBurnInLight; +@@ -299,7 +311,7 @@ public class Phantom extends FlyingMob implements Enemy { + if (compound.hasUUID("Paper.SpawningEntity")) { + this.spawningEntity = compound.getUUID("Paper.SpawningEntity"); + } +- if (compound.contains("Paper.ShouldBurnInDay")) { ++ if (false && compound.contains("Paper.ShouldBurnInDay")) { // Purpur - implemented in LivingEntity - API for any mob to burn daylight + this.shouldBurnInDay = compound.getBoolean("Paper.ShouldBurnInDay"); + } + // Paper end +@@ -316,7 +328,7 @@ public class Phantom extends FlyingMob implements Enemy { + if (this.spawningEntity != null) { + compound.putUUID("Paper.SpawningEntity", this.spawningEntity); + } +- compound.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); ++ //compound.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/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java +index 5d12bc139c81ca342074c7c745635669020d0300..f8d41ce860c5f48ab9b5e4b79a554dab85d2ba9b 100644 +--- a/net/minecraft/world/entity/monster/Zombie.java ++++ b/net/minecraft/world/entity/monster/Zombie.java +@@ -89,11 +89,12 @@ public class Zombie extends Monster { + private boolean canBreakDoors; + private int inWaterTime; + 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 entityType, Level level) { + super(entityType, level); + this.breakDoorGoal = new BreakDoorGoal(this, com.google.common.base.Predicates.in(level.paperConfig().entities.behavior.doorBreakingDifficulty.getOrDefault(entityType, 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) { +@@ -290,29 +291,7 @@ public class Zombie extends Monster { + + @Override + public void aiStep() { +- if (this.isAlive()) { +- boolean flag = this.isSunSensitive() && this.isSunBurnTick(); +- if (flag) { +- ItemStack itemBySlot = this.getItemBySlot(EquipmentSlot.HEAD); +- 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.HEAD); +- this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY); +- } +- } +- +- flag = false; +- } +- +- if (flag) { +- this.igniteForSeconds(8.0F); +- } +- } +- } +- ++ // Purpur - implemented in LivingEntity - API for any mob to burn daylight + super.aiStep(); + } + +@@ -371,6 +350,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 + } +@@ -509,7 +489,7 @@ public class Zombie extends Monster { + compound.putBoolean("CanBreakDoors", this.canBreakDoors()); + compound.putInt("InWaterTime", this.isInWater() ? this.inWaterTime : -1); + compound.putInt("DrownedConversionTime", this.isUnderWaterConverting() ? this.conversionTime : -1); +- compound.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper - Add more Zombie API ++ //compound.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); // Paper - Add more Zombie API // Purpur - implemented in LivingEntity - API for any mob to burn daylight + } + + @Override +@@ -522,7 +502,7 @@ public class Zombie extends Monster { + this.startUnderWaterConversion(compound.getInt("DrownedConversionTime")); + } + // Paper start - Add more Zombie API +- if (compound.contains("Paper.ShouldBurnInDay")) { ++ if (false && compound.contains("Paper.ShouldBurnInDay")) { // Purpur - implemented in LivingEntity - API for any mob to burn daylight + this.shouldBurnInDay = compound.getBoolean("Paper.ShouldBurnInDay"); + } + // Paper end - Add more Zombie API diff --git a/purpur-server/minecraft-patches/features/0020-Cows-naturally-aggressive-to-players-chance.patch b/purpur-server/minecraft-patches/features/0020-Cows-naturally-aggressive-to-players-chance.patch new file mode 100644 index 0000000000..9df4054e7b --- /dev/null +++ b/purpur-server/minecraft-patches/features/0020-Cows-naturally-aggressive-to-players-chance.patch @@ -0,0 +1,64 @@ +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/animal/Cow.java b/net/minecraft/world/entity/animal/Cow.java +index a8c76fcbbaa4afd2d0bd568874995b91d8d67c03..b62c4449047da36d8b4d4b87d03c77906d12dc31 100644 +--- a/net/minecraft/world/entity/animal/Cow.java ++++ b/net/minecraft/world/entity/animal/Cow.java +@@ -32,6 +32,8 @@ import net.minecraft.world.level.Level; + import net.minecraft.world.level.block.state.BlockState; + + public class Cow extends Animal { ++ private boolean isNaturallyAggressiveToPlayers; // Purpur - Cows naturally aggressive to players chance ++ + private static final EntityDimensions BABY_DIMENSIONS = EntityType.COW.getDimensions().scale(0.5F).withEyeHeight(0.665F); + + public Cow(EntityType entityType, Level level) { +@@ -60,6 +62,7 @@ public class Cow extends Animal { + public void initAttributes() { + this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level().purpurConfig.cowMaxHealth); + this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.cowScale); ++ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level().purpurConfig.cowNaturallyAggressiveToPlayersDamage); // Purpur - Cows naturally aggressive to players chance + } + // Purpur end - Configurable entity base attributes + +@@ -77,17 +80,27 @@ public class Cow extends Animal { + } + // Purpur end - Toggle for water sensitive mob damage + ++ // Purpur start - Cows naturally aggressive to players chance ++ @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.SpawnGroupData entityData) { ++ this.isNaturallyAggressiveToPlayers = world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance > 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.cowNaturallyAggressiveToPlayersChance; ++ return super.finalizeSpawn(world, difficulty, spawnReason, entityData); ++ } ++ // Purpur end - Cows naturally aggressive to players chance ++ + @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(1, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.2000000476837158D, true)); // Purpur - Cows naturally aggressive to players chance + 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 + 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)); + 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, (ignored, ignored2) -> isNaturallyAggressiveToPlayers)); // Purpur - Cows naturally aggressive to players chance + } + + @Override +@@ -96,7 +109,7 @@ public class Cow extends Animal { + } + + public static AttributeSupplier.Builder createAttributes() { +- return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 10.0).add(Attributes.MOVEMENT_SPEED, 0.2F); ++ return Animal.createAnimalAttributes().add(Attributes.MAX_HEALTH, 10.0).add(Attributes.MOVEMENT_SPEED, 0.2F).add(Attributes.ATTACK_DAMAGE, 0.0D); // Purpur - Cows naturally aggressive to players chance + } + + @Override diff --git a/purpur-server/minecraft-patches/features/0021-Mobs-always-drop-experience.patch b/purpur-server/minecraft-patches/features/0021-Mobs-always-drop-experience.patch new file mode 100644 index 0000000000..19c768b126 --- /dev/null +++ b/purpur-server/minecraft-patches/features/0021-Mobs-always-drop-experience.patch @@ -0,0 +1,1302 @@ +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/GlowSquid.java b/net/minecraft/world/entity/GlowSquid.java +index e717c063c8f9623b8c4b4ea3843d05fd79af3653..b982d4b7bdf39fcaf5f22cc889467d7b953e3a8e 100644 +--- a/net/minecraft/world/entity/GlowSquid.java ++++ b/net/minecraft/world/entity/GlowSquid.java +@@ -59,6 +59,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/ambient/Bat.java b/net/minecraft/world/entity/ambient/Bat.java +index 4fa526496265a85b637136f0fd0692ef4f570ad6..4ac052a78841939a53dac2afb575cb115581e249 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/Bee.java b/net/minecraft/world/entity/animal/Bee.java +index d4025093d82cca0c5923058dc0e35c91ae7b40e3..38c95287da10247b0627ce16ad4914232b5a6f06 100644 +--- a/net/minecraft/world/entity/animal/Bee.java ++++ b/net/minecraft/world/entity/animal/Bee.java +@@ -487,6 +487,13 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + } + // Purpur end - Make entity breeding times configurable + ++ // Purpur start - Mobs always drop experience ++ @Override ++ protected boolean isAlwaysExperienceDropper() { ++ return this.level().purpurConfig.beeAlwaysDropExp; ++ } ++ // Purpur end - Mobs always drop experience ++ + @Override + public boolean isSensitiveToWater() { + return this.level().purpurConfig.beeTakeDamageFromWater; +diff --git a/net/minecraft/world/entity/animal/Cat.java b/net/minecraft/world/entity/animal/Cat.java +index b41ca04043e65f107edaebc49d398650898e35fb..edd796fd34e43d66a48104201d885756fdd968c3 100644 +--- a/net/minecraft/world/entity/animal/Cat.java ++++ b/net/minecraft/world/entity/animal/Cat.java +@@ -140,6 +140,13 @@ public class Cat extends TamableAnimal implements VariantHolder itemStack.is(ItemTags.CAT_FOOD), true); +diff --git a/net/minecraft/world/entity/animal/Chicken.java b/net/minecraft/world/entity/animal/Chicken.java +index 2364596156c21e82879f5bf4fd873b9d90b1c308..4d0b172a9d54b1524c8052051859c7178774bef7 100644 +--- a/net/minecraft/world/entity/animal/Chicken.java ++++ b/net/minecraft/world/entity/animal/Chicken.java +@@ -95,6 +95,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/Cod.java b/net/minecraft/world/entity/animal/Cod.java +index ac7259cfc8428131f90956d7f76f2227049ffae3..b259de78198e0e3df9e5901019283ad246c8e044 100644 +--- a/net/minecraft/world/entity/animal/Cod.java ++++ b/net/minecraft/world/entity/animal/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/Cow.java b/net/minecraft/world/entity/animal/Cow.java +index b62c4449047da36d8b4d4b87d03c77906d12dc31..a0297ac3ba520122ed2095d6008c057d749b731e 100644 +--- a/net/minecraft/world/entity/animal/Cow.java ++++ b/net/minecraft/world/entity/animal/Cow.java +@@ -88,6 +88,13 @@ public class Cow extends Animal { + } + // Purpur end - Cows naturally aggressive to players chance + ++ // 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() { + this.goalSelector.addGoal(0, new FloatGoal(this)); +diff --git a/net/minecraft/world/entity/animal/Dolphin.java b/net/minecraft/world/entity/animal/Dolphin.java +index e104058b3c01bea4cc8a77de2ad4378465903b34..7003b532182737a745491e397a967b72e6b308aa 100644 +--- a/net/minecraft/world/entity/animal/Dolphin.java ++++ b/net/minecraft/world/entity/animal/Dolphin.java +@@ -163,6 +163,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 ++ + @Nullable + @Override + public SpawnGroupData finalizeSpawn( +diff --git a/net/minecraft/world/entity/animal/Fox.java b/net/minecraft/world/entity/animal/Fox.java +index 22a70c6af965114e272bb56cb217f975a3cd1bd4..aa610af9db105081fcabc1f299e5f2dd1f4d907e 100644 +--- a/net/minecraft/world/entity/animal/Fox.java ++++ b/net/minecraft/world/entity/animal/Fox.java +@@ -189,6 +189,13 @@ public class Fox extends Animal implements VariantHolder { + } + // 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/IronGolem.java b/net/minecraft/world/entity/animal/IronGolem.java +index 23108729ce65ef8b7b215b82f29347513cfd4ebf..ccadc9a151e258ff2c74c65c374b1f09d56d10ec 100644 +--- a/net/minecraft/world/entity/animal/IronGolem.java ++++ b/net/minecraft/world/entity/animal/IronGolem.java +@@ -105,6 +105,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/MushroomCow.java b/net/minecraft/world/entity/animal/MushroomCow.java +index 5e9795f447e88a42909730d383eaa36acfaf18f5..3bcd119757dfc579df790fcc8919a3636bafa7fe 100644 +--- a/net/minecraft/world/entity/animal/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/MushroomCow.java +@@ -93,6 +93,13 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder { + } + // 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/Salmon.java b/net/minecraft/world/entity/animal/Salmon.java +index 8bd4b2b29438bff65ed00a42bbc9639111af181f..5da2f14770aebb2286c3e8cbd9622a89a33e0e20 100644 +--- a/net/minecraft/world/entity/animal/Salmon.java ++++ b/net/minecraft/world/entity/animal/Salmon.java +@@ -61,6 +61,13 @@ public class Salmon extends AbstractSchoolingFish implements VariantHolder, B + } + // 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/goat/Goat.java b/net/minecraft/world/entity/animal/goat/Goat.java +index b95570519301bb9e260ce3c20f3231c79ff22fac..5603b13e7f8a4f61e63900c93f9524347e288efa 100644 +--- a/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/net/minecraft/world/entity/animal/goat/Goat.java +@@ -142,6 +142,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/horse/Donkey.java b/net/minecraft/world/entity/animal/horse/Donkey.java +index 1db6ccdc6c83c704aa84a46ee2751a17125bf457..3e0181578a6f2d22d1da3776abf30bf97d124620 100644 +--- a/net/minecraft/world/entity/animal/horse/Donkey.java ++++ b/net/minecraft/world/entity/animal/horse/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 + protected SoundEvent getAmbientSound() { + return SoundEvents.DONKEY_AMBIENT; +diff --git a/net/minecraft/world/entity/animal/horse/Horse.java b/net/minecraft/world/entity/animal/horse/Horse.java +index f257d549570918381925cef98734fc0aa605f8f2..be0d636ca894c5995f28f59c196cd8e56dd228c4 100644 +--- a/net/minecraft/world/entity/animal/horse/Horse.java ++++ b/net/minecraft/world/entity/animal/horse/Horse.java +@@ -81,6 +81,13 @@ public class Horse extends AbstractHorse implements VariantHolder { + } + // 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/horse/Llama.java b/net/minecraft/world/entity/animal/horse/Llama.java +index 872a54186a20fd855fe7981f3ff1c867f4c64d19..c21d558a6a3a61d6c54b8163f8cb4963846b2c26 100644 +--- a/net/minecraft/world/entity/animal/horse/Llama.java ++++ b/net/minecraft/world/entity/animal/horse/Llama.java +@@ -155,6 +155,13 @@ public class Llama extends AbstractChestedHorse implements VariantHolder entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/Illusioner.java b/net/minecraft/world/entity/monster/Illusioner.java +index ad661f2bf8957644605b52a469d6a7cf8e064398..9686658b90e886d6236f553d7406771814d18672 100644 +--- a/net/minecraft/world/entity/monster/Illusioner.java ++++ b/net/minecraft/world/entity/monster/Illusioner.java +@@ -91,6 +91,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/MagmaCube.java b/net/minecraft/world/entity/monster/MagmaCube.java +index bf26f5f6017c60d5991d5f6c87da2acbd95ef5bb..312d4a3d312b5c326d6ca13ccfc48171e18f4370 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 0ee817699fffbb929011465029182cc56befc30c..39f94cee78e8fc14d892cb64fb5234bf88d964ad 100644 +--- a/net/minecraft/world/entity/monster/Phantom.java ++++ b/net/minecraft/world/entity/monster/Phantom.java +@@ -157,6 +157,13 @@ public class Phantom extends FlyingMob implements Enemy { + public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } + // Purpur end - API for any mob to burn daylight + ++ // 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/Pillager.java b/net/minecraft/world/entity/monster/Pillager.java +index 869a1007de13f3f5d757968d0f84cbf43786c870..a57d869cdc6a05124237933437aa2d26ff72cab3 100644 +--- a/net/minecraft/world/entity/monster/Pillager.java ++++ b/net/minecraft/world/entity/monster/Pillager.java +@@ -95,6 +95,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/Ravager.java b/net/minecraft/world/entity/monster/Ravager.java +index 2844846811398350832a0f88a72772831e3e43d5..4dc2b324183254753b6b3269a6ef3ec2f5c2ee2d 100644 +--- a/net/minecraft/world/entity/monster/Ravager.java ++++ b/net/minecraft/world/entity/monster/Ravager.java +@@ -104,6 +104,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 c26e4858a14571d58e439cabd5f2593da4ee2634..a006300aea2cbb05400550f1c79e872d095384f8 100644 +--- a/net/minecraft/world/entity/monster/Shulker.java ++++ b/net/minecraft/world/entity/monster/Shulker.java +@@ -136,6 +136,13 @@ public class Shulker extends AbstractGolem implements VariantHolder entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random + ) { +diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index d01bc46d77340b10b23d0c0d50bddc37657028c8..241526239bdbd5d9276f85e7fca46a7051f46a25 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -126,6 +126,13 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { + } + // 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 ffdb200d9716104f8df91dbeef590b2264e587b8..0ce9eb1f4108b6afab6df84d5e709be0882e515a 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/Vindicator.java b/net/minecraft/world/entity/monster/Vindicator.java +index 5f68d73460adfac2ead57d168156a2784af979ae..b584f71440a81ac09d24e59763a21e857f290e5a 100644 +--- a/net/minecraft/world/entity/monster/Vindicator.java ++++ b/net/minecraft/world/entity/monster/Vindicator.java +@@ -87,6 +87,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/Witch.java b/net/minecraft/world/entity/monster/Witch.java +index 96ba35f3530ab405a960c79955699666deb6b845..e4353c64732067198f082cdd266c1f1ee1fe4f4e 100644 +--- a/net/minecraft/world/entity/monster/Witch.java ++++ b/net/minecraft/world/entity/monster/Witch.java +@@ -88,6 +88,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/WitherSkeleton.java b/net/minecraft/world/entity/monster/WitherSkeleton.java +index a4dc9b7fbde19b08eb389dc42df21aa5df94e703..ff2596f69d00b36c65872ab2e27e5d44a6ffa3e1 100644 +--- a/net/minecraft/world/entity/monster/WitherSkeleton.java ++++ b/net/minecraft/world/entity/monster/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/Zoglin.java b/net/minecraft/world/entity/monster/Zoglin.java +index 79a4a3f4e10e1f9c1a6100060a95636075fc8236..d2a67f3e1c971f737e58567dae23fa70e9d942ea 100644 +--- a/net/minecraft/world/entity/monster/Zoglin.java ++++ b/net/minecraft/world/entity/monster/Zoglin.java +@@ -117,6 +117,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/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java +index f8d41ce860c5f48ab9b5e4b79a554dab85d2ba9b..26b57ffdc08a851e4e5cbdeebcc19d62517ba9fa 100644 +--- a/net/minecraft/world/entity/monster/Zombie.java ++++ b/net/minecraft/world/entity/monster/Zombie.java +@@ -147,6 +147,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/ZombieVillager.java b/net/minecraft/world/entity/monster/ZombieVillager.java +index 94b9abc765b78a40a7ecbf4cbd775b778d49c815..1ca0514732916d325c4a76d73120aaf613c3f780 100644 +--- a/net/minecraft/world/entity/monster/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/ZombieVillager.java +@@ -131,6 +131,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/ZombifiedPiglin.java b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +index 09c991d8e344f11bc84dea453042ee35c39e580e..fddbbffafea275dad187b7908386cf4c05c86743 100644 +--- a/net/minecraft/world/entity/monster/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +@@ -112,6 +112,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 + public void setPersistentAngerTarget(@Nullable UUID target) { + this.persistentAngerTarget = target; +diff --git a/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index 896bd531a5333d6dc8996bbdfc5c878b1a7d2da0..cf0a15ff4a49bcc17dc6dd58e91eadec0c519455 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 ++ + @Override + public boolean canBeLeashed() { + return true; +diff --git a/net/minecraft/world/entity/monster/piglin/Piglin.java b/net/minecraft/world/entity/monster/piglin/Piglin.java +index 55ebfa5df06a59203248514d10dced9660ebf215..f0d78cf5fe2c39add9a673471103c352cce72a45 100644 +--- a/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -183,6 +183,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 + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); +diff --git a/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/net/minecraft/world/entity/monster/piglin/PiglinBrute.java +index 2237681f298113bda0556699e19e880f4b04a853..4984b9864b63f92bc939b530253e871ca94a3277 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/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java +index af6ae76248a9894efbecf9e94160f8d215f6ad85..92261f502de7b832845b6e6a12a091801096dc4d 100644 +--- a/net/minecraft/world/entity/npc/Villager.java ++++ b/net/minecraft/world/entity/npc/Villager.java +@@ -285,6 +285,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.java b/net/minecraft/world/entity/npc/WanderingTrader.java +index 5a87c3c7aca38f3fe4a003b2075f43b0ae1cddea..63a83fb3b103b81dad26c353babfde2e35636f53 100644 +--- a/net/minecraft/world/entity/npc/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/WanderingTrader.java +@@ -114,6 +114,13 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill + } + // 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/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 0000000000..cca45cce00 --- /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 +@@ -141,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); +@@ -282,6 +_,7 @@ + * @return + */ + public static boolean checkIfActive(final Entity entity) { ++ if (entity.level().purpurConfig.squidImmuneToEAR && entity instanceof net.minecraft.world.entity.animal.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 0000000000..52cafd0d1e --- /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 + } + + @Nullable +@@ -161,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 0000000000..1a48edcf98 --- /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 +@@ -455,6 +_,19 @@ + } + // CraftBukkit end + ++ // Purpur start - Gamemode extra permissions ++ 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 - Gamemode extra permissions ++ + public Vec3 getPosition() { + return this.worldPosition; + } +@@ -540,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(@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 - 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 0000000000..bd8ac4ec4f --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/commands/Commands.java.patch @@ -0,0 +1,52 @@ +--- a/net/minecraft/commands/Commands.java ++++ b/net/minecraft/commands/Commands.java +@@ -137,6 +_,7 @@ + import net.minecraft.world.flag.FeatureFlagSet; + import net.minecraft.world.flag.FeatureFlags; + import net.minecraft.world.level.GameRules; ++import org.bukkit.event.player.PlayerCommandSendEvent; + import org.slf4j.Logger; + + public class Commands { +@@ -216,8 +_,8 @@ + JfrCommand.register(this.dispatcher); + } + +- if (SharedConstants.IS_RUNNING_IN_IDE) { +- TestCommand.register(this.dispatcher); ++ if (org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands || SharedConstants.IS_RUNNING_IN_IDE) { // Purpur - register minecraft debug commands ++ if (!org.purpurmc.purpur.PurpurConfig.registerMinecraftDebugCommands) TestCommand.register(this.dispatcher); // Purpur - register minecraft debug commands + RaidCommand.register(this.dispatcher, context); + DebugPathCommand.register(this.dispatcher); + DebugMobSpawningCommand.register(this.dispatcher); +@@ -245,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) { +@@ -488,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 (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); + +@@ -498,6 +_,7 @@ + } + } + // CraftBukkit end ++ } // Purpur - Skip events if there's no listeners + + player.connection.send(new ClientboundCommandsPacket(rootCommandNode)); + } 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 0000000000..c89e997d6f --- /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 +@@ -192,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; +@@ -270,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 0000000000..8b669b1d6a --- /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 +@@ -63,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 0000000000..067298651e --- /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 +@@ -892,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 0000000000..c0e69aa51d --- /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 +@@ -31,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 0000000000..2076037b3a --- /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 +@@ -279,6 +_,8 @@ + return gameType.isCreative(); + } + ++ public void setAfk(final boolean afk) {} // Purpur - AFK API ++ + @Override + public boolean isLocalPlayer() { + return true; 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 0000000000..ac6b9a64d5 --- /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 +@@ -588,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 0000000000..60c29bda0e --- /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 +@@ -45,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 0000000000..16550b7e6e --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/Main.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/server/Main.java ++++ b/net/minecraft/server/Main.java +@@ -108,6 +_,12 @@ + 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 ++ // 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 0000000000..a3769afc71 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/MinecraftServer.java.patch @@ -0,0 +1,133 @@ +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -284,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 +@@ -294,7 +_,7 @@ + public static final int TICK_TIME = 1000000000 / MinecraftServer.TPS; + private static final int SAMPLE_INTERVAL = 20; // Paper - improve server tick loop + @Deprecated(forRemoval = true) // Paper +- public final double[] recentTps = new double[3]; ++ public final double[] recentTps = new double[4]; // Purpur - Add 5 second tps average in /tps + // Spigot end + public volatile boolean hasFullyShutdown; // Paper - Improved watchdog support + public volatile boolean abnormalExit; // Paper - Improved watchdog support +@@ -302,7 +_,9 @@ + public final io.papermc.paper.configuration.PaperConfigurations paperConfigurations; // Paper - add paper configuration files + 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 boolean lagging = false; // Purpur - Lagging threshold + public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation ++ protected boolean upnp = false; // Purpur - UPnP Port Forwarding + + public static S spin(Function threadFunction) { + ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system +@@ -1001,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 +@@ -1093,6 +_,8 @@ + this.safeShutdown(waitForServer, false); + } + public void safeShutdown(boolean waitForServer, 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 +@@ -1112,6 +_,7 @@ + private static final long MAX_CATCHUP_BUFFER = TICK_TIME * TPS * 60L; + private long lastTick = 0; + private long catchupTime = 0; ++ public final RollingAverage tps5s = new RollingAverage(5); // Purpur - Add 5 second tps average in /tps + public final RollingAverage tps1 = new RollingAverage(60); + public final RollingAverage tps5 = new RollingAverage(60 * 5); + public final RollingAverage tps15 = new RollingAverage(60 * 15); +@@ -1197,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) { + long l; + if (!this.isPaused() && this.tickRateManager.isSprinting() && this.tickRateManager.checkShouldSprintThisTick()) { +@@ -1221,14 +_,19 @@ + if (++MinecraftServer.currentTick % MinecraftServer.SAMPLE_INTERVAL == 0) { + final long diff = currentTime - tickSection; + final java.math.BigDecimal currentTps = TPS_BASE.divide(new java.math.BigDecimal(diff), 30, java.math.RoundingMode.HALF_UP); ++ tps5s.add(currentTps, diff); // Purpur - Add 5 second tps average in /tps + tps1.add(currentTps, diff); + tps5.add(currentTps, diff); + tps15.add(currentTps, diff); + + // Backwards compat with bad plugins +- this.recentTps[0] = tps1.getAverage(); +- this.recentTps[1] = tps5.getAverage(); +- this.recentTps[2] = tps15.getAverage(); ++ // Purpur start - Add 5 second tps average in /tps ++ this.recentTps[0] = tps5s.getAverage(); ++ this.recentTps[1] = tps1.getAverage(); ++ this.recentTps[2] = tps5.getAverage(); ++ this.recentTps[3] = tps15.getAverage(); ++ // Purpur end - Add 5 second tps average in /tps ++ lagging = recentTps[0] < org.purpurmc.purpur.PurpurConfig.laggingThreshold; // Purpur - Lagging threshold + tickSection = currentTime; + } + // Paper end - further improve server tick loop +@@ -1260,6 +_,12 @@ + profilerFiller.popPush("nextTickWait"); + this.mayHaveDelayedTasks = true; + this.delayedTasksMaxNextTickTimeNanos = Math.max(Util.getNanos() + l, this.nextTickTimeNanos); ++ // Purpur start - Configurable TPS Catchup ++ if (!org.purpurmc.purpur.PurpurConfig.tpsCatchup /*|| !gg.pufferfish.pufferfish.PufferfishConfig.tpsCatchup*/) { // Purpur - Configurable TPS Catchup ++ this.nextTickTimeNanos = currentTime + l; ++ this.delayedTasksMaxNextTickTimeNanos = nextTickTimeNanos; ++ } ++ // Purpur end - Configurable TPS Catchup + this.startMeasuringTaskExecutionTime(); + this.waitUntilNextTick(); + this.finishMeasuringTaskExecutionTime(); +@@ -1690,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; +@@ -1855,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 0000000000..423015dc47 --- /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 +@@ -148,6 +_,7 @@ + AdvancementHolder advancementHolder = advancementManager.get(path); + if (advancementHolder == null) { + if (!path.getNamespace().equals(ResourceLocation.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); +@@ -195,6 +_,7 @@ + advancement.value().display().ifPresent(displayInfo -> { + // Paper start - Add Adventure message to PlayerAdvancementDoneEvent + if (event.message() != null && this.player.serverLevel().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) { ++ 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 0000000000..74bd1f7101 --- /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; +@@ -81,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 0000000000..b1ef32c8b8 --- /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 +@@ -51,6 +_,18 @@ + } + + private static int setMode(CommandContext source, Collection players, GameType gameType) { ++ // Purpur start - Gamemode extra permissions ++ if (org.purpurmc.purpur.PurpurConfig.commandGamemodeRequiresPermission) { ++ String gamemode = gameType.getName(); ++ CommandSourceStack sender = source.getSource(); ++ if (!sender.testPermission(2, "minecraft.command.gamemode." + gamemode)) { ++ return 0; ++ } ++ if (sender.getEntity() instanceof ServerPlayer player && (players.size() > 1 || !players.contains(player)) && !sender.testPermission(2, "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 0000000000..5cedf347fc --- /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 +@@ -66,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); // CraftBukkit - SPIGOT-2942: Add boolean to call event + 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 0000000000..2eb72430f7 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -0,0 +1,66 @@ +--- a/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/net/minecraft/server/dedicated/DedicatedServer.java +@@ -106,6 +_,7 @@ + // CraftBukkit start + if (!org.bukkit.craftbukkit.Main.useConsole) return; + // 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(); + /* + jline.console.ConsoleReader bufferedreader = DedicatedServer.this.reader; +@@ -224,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.setPvpAllowed(properties.pvp); +@@ -271,6 +_,30 @@ + if (true) throw new IllegalStateException("Failed to bind to port", var10); // 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.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); // Spigot - moved up +@@ -350,6 +_,8 @@ + 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 + 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 0000000000..14576e237b --- /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 +@@ -49,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 boolean pvp = this.get("pvp", true); + public final boolean allowFlight = this.get("allow-flight", false); + public final String motd = this.get("motd", "A Minecraft Server"); 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 0000000000..910e0b8824 --- /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 0000000000..a082bca562 --- /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 +@@ -43,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 0000000000..a4cf791606 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerLevel.java.patch @@ -0,0 +1,172 @@ +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -207,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; + + // CraftBukkit start +@@ -595,7 +_,24 @@ + // CraftBukkit end + this.tickTime = tickTime; + this.server = server; +- this.customSpawners = customSpawners; ++ // Purpur start - Allow toggling special MobSpawners per world ++ this.customSpawners = new ArrayList<>(); ++ 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.WanderingTraderSpawner(serverLevelData)); ++ } ++ // Purpur end - Allow toggling special MobSpawners per world + this.serverLevelData = serverLevelData; + ChunkGenerator chunkGenerator = levelStem.generator(); + // CraftBukkit start +@@ -681,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 +@@ -727,7 +_,7 @@ + } + + int _int = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); +- if (this.sleepStatus.areEnoughSleeping(_int) && this.sleepStatus.areEnoughDeepSleeping(_int, this.players)) { ++ if (this.purpurConfig.playersSkipNight && this.sleepStatus.areEnoughSleeping(_int) && this.sleepStatus.areEnoughDeepSleeping(_int, 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( +@@ -846,6 +_,13 @@ + this.serverLevelData.getScheduledEvents().tick(this.server, l); + Profiler.get().pop(); + if (this.serverLevelData.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) { ++ // Purpur start - Configurable daylight cycle ++ int incrementTicks = isDay() ? 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); + } + } +@@ -853,7 +_,21 @@ + + 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 void tickCustomSpawners(boolean spawnEnemies, boolean spawnFriendlies) { + for (CustomSpawner customSpawner : this.customSpawners) { +@@ -934,9 +_,18 @@ + && 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(Blocks.LIGHTNING_ROD); + if (flag) { ++ // Purpur start - Special mobs naturally spawn ++ net.minecraft.world.entity.animal.horse.AbstractHorse entityhorseskeleton; ++ if (purpurConfig.zombieHorseSpawnChance > 0D && random.nextDouble() <= purpurConfig.zombieHorseSpawnChance) { ++ entityhorseskeleton = EntityType.ZOMBIE_HORSE.create(this, EntitySpawnReason.EVENT); ++ } else { ++ entityhorseskeleton = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT); ++ if (entityhorseskeleton != null) ((SkeletonHorse) entityhorseskeleton).setTrap(true); ++ } ++ // Purpur end - Special mobs naturally spawn + SkeletonHorse skeletonHorse = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT); + 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 +@@ -1009,7 +_,7 @@ + pointOfInterestType -> pointOfInterestType.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)); +@@ -1057,8 +_,26 @@ + int _int = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); + Component component; + if (this.sleepStatus.areEnoughSleeping(_int)) { ++ // 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(_int))))); ++ } else ++ // Purpur end - Customizable sleeping actionbar messages + component = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(_int)); + } + +@@ -1191,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.... +@@ -1198,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. +@@ -2676,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 0000000000..fc98a0e357 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -0,0 +1,276 @@ +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -393,6 +_,10 @@ + public com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent + public @Nullable String clientBrandName = null; // Paper - Brand support + public org.bukkit.event.player.PlayerQuitEvent.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 ++ public boolean purpurClient = false; // Purpur - Purpur client support ++ 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; +@@ -561,6 +_,10 @@ + if (tag != null) { + BlockPos.CODEC.parse(NbtOps.INSTANCE, tag).resultOrPartial(LOGGER::error).ifPresent(pos -> this.raidOmenPosition = pos); + } ++ ++ if (compound.contains("Purpur.TPSBar")) { this.tpsBar = compound.getBoolean("Purpur.TPSBar"); } // Purpur - Implement TPSBar ++ if (compound.contains("Purpur.CompassBar")) { this.compassBar = compound.getBoolean("Purpur.CompassBar"); } // Purpur - Add compass command ++ if (compound.contains("Purpur.RamBar")) { this.ramBar = compound.getBoolean("Purpur.RamBar"); } // Purpur - Implement rambar command + } + + @Override +@@ -605,6 +_,9 @@ + } + + this.saveEnderPearls(compound); ++ compound.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur - Implement TPSBar ++ compound.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur - Add compass command ++ compound.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur - Add rambar command + } + + private void saveParentVehicle(CompoundTag tag) { +@@ -1124,6 +_,7 @@ + ) + ); + 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) { +@@ -1217,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 && !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)) +@@ -1446,6 +_,7 @@ + serverLevel.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION); + this.unsetRemoved(); + // CraftBukkit end ++ this.portalPos = io.papermc.paper.util.MCUtil.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(); +@@ -1564,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.serverLevel(), 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); + } + } +@@ -1601,7 +_,19 @@ + CriteriaTriggers.SLEPT_IN_BED.trigger(this); + }); + if (!this.serverLevel().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 + } + + ((ServerLevel)this.level()).updateSleepingPlayerList(); +@@ -1709,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)); + } +@@ -2014,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(@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 - Component related conveniences ++ + @Override + public void displayClientMessage(Component chatComponent, boolean actionBar) { + this.sendSystemMessage(chatComponent, actionBar); +@@ -2235,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(@Nullable net.kyori.adventure.text.Component message) { ++ if (message != null) { ++ this.sendSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message)); ++ } ++ } ++ // Purpur end - Component related conveniences ++ + public void sendSystemMessage(Component mesage) { + this.sendSystemMessage(mesage, false); + } +@@ -2373,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().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, true); ++ } ++ } ++ ++ ((ServerLevel) this.level()).updateSleepingPlayerList(); ++ } ++ ++ @Override ++ public boolean isAfk() { ++ return this.isAfk; ++ } ++ ++ @Override ++ public boolean canBeCollidedWith() { ++ return !this.isAfk() && super.canBeCollidedWith(); ++ } ++ // Purpur end - AFK API + + public ServerStatsCounter getStats() { + return this.stats; +@@ -3078,4 +_,56 @@ + 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.teleport(to); ++ } else { ++ this.server.getPlayerList().respawn(this, true, RemovalReason.KILLED, org.bukkit.event.player.PlayerRespawnEvent.RespawnReason.DEATH, to); ++ } ++ } ++ // 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 0000000000..2586f46dfb --- /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 +@@ -351,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 + +@@ -464,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; +@@ -506,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); +@@ -552,4 +_,18 @@ + public void setLevel(ServerLevel serverLevel) { + this.level = serverLevel; + } ++ ++ // 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(-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 - 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 0000000000..fe8a951244 --- /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 +@@ -312,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 0000000000..4405b92452 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch @@ -0,0 +1,72 @@ +--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +@@ -41,6 +_,7 @@ + private long keepAliveChallenge; + 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 int latency; + private volatile boolean suspendFlushingOnServerThread = false; + // CraftBukkit start +@@ -51,6 +_,7 @@ + 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.ResourceLocation MINECRAFT_BRAND = net.minecraft.resources.ResourceLocation.withDefaultNamespace("brand"); // Paper - Brand support ++ protected static final net.minecraft.resources.ResourceLocation PURPUR_CLIENT = net.minecraft.resources.ResourceLocation.fromNamespaceAndPath("purpur", "client"); // Purpur - Purpur client support + + public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie, net.minecraft.server.level.ServerPlayer player) { // CraftBukkit + this.server = server; +@@ -118,6 +_,16 @@ + + @Override + public void handleKeepAlive(ServerboundKeepAlivePacket packet) { ++ // 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()); ++ this.latency = (this.latency * 3 + ping) / 4; ++ this.keepAlivePending = false; ++ keepAlives.clear(); // we got a valid response, lets roll with it and forget the rest ++ } ++ } else ++ // Purpur end - Alternative Keepalive Handling + if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) { + int i = (int)(Util.getMillis() - this.keepAliveTime); + this.latency = (this.latency * 3 + i) / 4; +@@ -159,6 +_,13 @@ + ServerGamePacketListenerImpl.LOGGER.error("Couldn't register custom payload", ex); + this.disconnect(Component.literal("Invalid payload REGISTER!"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause + } ++ // Purpur start - Purpur client support ++ } else if (identifier.equals(PURPUR_CLIENT)) { ++ try { ++ player.purpurClient = true; ++ } catch (Exception ignore) { ++ } ++ // Purpur end - Purpur client support + } else if (identifier.equals(ServerCommonPacketListenerImpl.CUSTOM_UNREGISTER)) { + try { + String channels = payload.toString(com.google.common.base.Charsets.UTF_8); +@@ -238,6 +_,22 @@ + // 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 + final long elapsedTime = millis - this.keepAliveTime; ++ ++ // Purpur start - Alternative Keepalive Handling ++ if (org.purpurmc.purpur.PurpurConfig.useAlternateKeepAlive) { ++ if (elapsedTime >= 1000L) { // 1 second ++ if (this.keepAlivePending && !this.processedDisconnect && keepAlives.size() * 1000L >= KEEPALIVE_LIMIT) { ++ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); ++ } else if (this.checkIfClosed(millis)) { ++ this.keepAlivePending = true; ++ this.keepAliveTime = millis; // hijack this field for 1 second intervals ++ this.keepAlives.add(millis); // currentTime is ID ++ this.send(new ClientboundKeepAlivePacket(millis)); ++ } ++ } ++ } else ++ // Purpur end - Alternative Keepalive Handling ++ + if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // use vanilla's 15000L between keep alive packets + if (this.keepAlivePending) { + if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected 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 0000000000..e22ca0e6aa --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -0,0 +1,224 @@ +--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -326,6 +_,20 @@ + this.chatMessageChain = new FutureChain(server.chatExecutor); // CraftBukkit - async chat + } + ++ // 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) { +@@ -384,6 +_,12 @@ + if (this.player.getLastActionTime() > 0L + && this.server.getPlayerIdleTimeout() > 0 + && Util.getMillis() - this.player.getLastActionTime() > this.server.getPlayerIdleTimeout() * 1000L * 60L && !this.player.wonGame) { // Paper - Prevent AFK kick while watching end credits ++ // 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.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854 + this.disconnect(Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause + } +@@ -629,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); +@@ -709,6 +_,7 @@ + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); + 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; + } +@@ -1169,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; +@@ -1193,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; + } +@@ -1212,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) +- : texts -> this.updateBookContents(texts, slot); ++ ? texts -> this.signBook(texts.get(0), texts.subList(1, texts.size()), slot, hasSignPerm) // Purpur - Allow color codes in books ++ : texts -> this.updateBookContents(texts, 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.getName().getString(), 0, list, true) +@@ -1250,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.serverLevel()); +@@ -1285,7 +_,15 @@ + @Override + public void handleMovePlayer(ServerboundMovePlayerPacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); +- 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.serverLevel(); +@@ -1460,7 +_,7 @@ + movedWrongly = true; + if (event.getLogWarning()) + // Paper end +- LOGGER.warn("{} moved wrongly!", this.player.getName().getString()); ++ LOGGER.warn("{} moved wrongly!, ({})", this.player.getName().getString(), verticalDelta); // Purpur - AFK API + } // Paper + } + +@@ -1526,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); +@@ -1582,6 +_,13 @@ + this.player.tryResetCurrentImpulseContext(); + } + ++ // Purpur start - Dont run with scissors! ++ if (this.player.serverLevel().purpurConfig.dontRunWithScissors && this.player.isSprinting() && !(this.player.serverLevel().purpurConfig.ignoreScissorsInWater && this.player.isInWater()) && !(this.player.serverLevel().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.serverLevel(), this.player.damageSources().scissors(), (float) this.player.serverLevel().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(); +@@ -1630,6 +_,17 @@ + } + } + ++ // Purpur start - Dont run with scissors! ++ public boolean isScissors(ItemStack stack) { ++ if (!stack.is(Items.SHEARS)) return false; ++ ++ ResourceLocation itemModelReference = stack.get(net.minecraft.core.component.DataComponents.ITEM_MODEL); ++ if (itemModelReference != null && itemModelReference.equals(this.player.serverLevel().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! ++ + // Paper start - optimise out extra getCubes + private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) { + final List collisionsBB = new java.util.ArrayList<>(); +@@ -1994,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 { +@@ -2734,6 +_,7 @@ + + AABB boundingBox = target.getBoundingBox(); + if (this.player.canInteractWithEntity(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 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 0000000000..5b3bc1b3f1 --- /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 +@@ -307,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 0000000000..3fc9d014a0 --- /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 0000000000..1818a74dfd --- /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 +@@ -396,6 +_,7 @@ + scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam); + } + // Paper end - Configurable player collision ++ org.purpurmc.purpur.task.BossBarTask.addToAll(player); // Purpur - Implement TPSBar + PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", player.getName().getString(), loggableAddress, player.getId(), serverLevel.serverLevelData.getLevelName(), player.getX(), player.getY(), player.getZ()); + // Paper start - Send empty chunk, so players aren't stuck in the world loading screen with our chunk system not sending chunks when dead + if (player.isDeadOrDying()) { +@@ -501,6 +_,7 @@ + } + public net.kyori.adventure.text.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.serverLevel(); + player.awardStat(Stats.LEAVE_GAME); + // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it +@@ -665,7 +_,7 @@ + // return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameProfile) + // ? Component.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 - Allow player join full server by permission + event.disallow(org.bukkit.event.player.PlayerLoginEvent.Result.KICK_FULL, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.serverFullMessage)); // Spigot // Paper - Adventure + } + } +@@ -919,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(@Nullable net.kyori.adventure.text.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) { +@@ -1002,6 +_,7 @@ + } else { + b = (byte)(24 + permLevel); + } ++ if (b < 28 && player.getBukkitEntity().hasPermission("purpur.debug.f3n")) b = 28; // Purpur - Add permission for F3+N debug + + player.connection.send(new ClientboundEntityEventPacket(player, b)); + } 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 0000000000..998d2f0578 --- /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 0000000000..384d2c33b3 --- /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 +@@ -138,6 +_,7 @@ + try { + ResourceKey> resourceKey = ResourceKey.create(Registries.RECIPE, ResourceLocation.parse(string)); + 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 0000000000..b8d2311826 --- /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 0000000000..a70c7a2536 --- /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 damage, DamageSource damageSource, float armorValue, float armorToughness) { + float f = 2.0F + armorToughness / 4.0F; +- float f1 = Mth.clamp(armorValue - damage / f, armorValue * 0.2F, 20.0F); ++ float f1 = Mth.clamp(armorValue - damage / 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 damage, 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 damage * (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 0000000000..0994f6fcdc --- /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 +@@ -54,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); + } +@@ -98,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 0000000000..4fe0b6a162 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/DamageSource.java.patch @@ -0,0 +1,70 @@ +--- a/net/minecraft/world/damagesource/DamageSource.java ++++ b/net/minecraft/world/damagesource/DamageSource.java +@@ -28,6 +_,8 @@ + private boolean sweep = false; + private boolean melting = false; + private boolean poison = false; ++ private boolean scissors = false; // Purpur - Dont run with scissors! ++ private boolean stonecutter = false; // Purpur - Stonecutter damage + @Nullable + private Entity customEventDamager = null; // This field is a helper for when causing entity damage is not set by vanilla // Paper - fix DamageSource API + +@@ -58,6 +_,27 @@ + return this.poison; + } + ++ // Purpur start - Dont run with scissors! ++ public DamageSource scissors() { ++ 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.stonecutter = true; ++ return this; ++ } ++ ++ public boolean isStonecutter() { ++ return this.stonecutter; ++ } ++ // Purpur end - Stonecutter damage ++ + // Paper start - fix DamageSource API + @Nullable + public Entity getCustomEventDamager() { +@@ -118,6 +_,8 @@ + damageSource.sweep = this.isSweep(); + damageSource.poison = this.isPoison(); + damageSource.melting = this.isMelting(); ++ damageSource.scissors = this.isScissors(); // Purpur - Dont run with scissors! ++ damageSource.stonecutter = this.isStonecutter(); // Purpur - Stonecutter damage + return damageSource; + } + // CraftBukkit end +@@ -184,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 0000000000..9e3c63dab0 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/damagesource/DamageSources.java.patch @@ -0,0 +1,36 @@ +--- a/net/minecraft/world/damagesource/DamageSources.java ++++ b/net/minecraft/world/damagesource/DamageSources.java +@@ -45,11 +_,15 @@ + // CraftBukkit start + private final DamageSource melting; + private final DamageSource poison; ++ 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); + this.melting = this.source(DamageTypes.ON_FIRE).melting(); + this.poison = this.source(DamageTypes.MAGIC).poison(); ++ this.scissors = this.source(DamageTypes.MAGIC).scissors(); // Purpur - Dont run with scissors! ++ this.stonecutter = this.source(DamageTypes.MAGIC).stonecutter(); // Purpur - Stonecutter damage + // CraftBukkit end + this.inFire = this.source(DamageTypes.IN_FIRE); + this.campfire = this.source(DamageTypes.CAMPFIRE); +@@ -100,6 +_,17 @@ + } + // CraftBukkit end + ++ // Purpur start - Dont run with scissors! ++ 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 0000000000..ea284de7b8 --- /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 0000000000..48b0053a0e --- /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().poison(), 1.0F); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON ++ if (entity.getHealth() > entity.level().purpurConfig.entityMinimalHealthPoison) { // Purpur ++ entity.hurtServer(level, entity.damageSources().poison(), entity.level().purpurConfig.entityPoisonDegenerationAmount); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON // 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 0000000000..f19f319af4 --- /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 0000000000..f2d21d3808 --- /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 0000000000..7676376597 --- /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 0000000000..d8e53c8f7a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch @@ -0,0 +1,186 @@ +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -136,7 +_,7 @@ + import org.slf4j.Logger; + + public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess, ScoreHolder, 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; + public boolean preserveMotion = true; // Paper - Fix Entity Teleportation and cancel velocity if teleported; keep initial motion on first setPositionRotation +@@ -253,9 +_,10 @@ + public double xOld; + public double yOld; + public double zOld; ++ public float maxUpStep; // Purpur - Add option to set armorstand step height + public boolean noPhysics; + private boolean wasOnFire; +- 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 = -this.getFireImmuneTicks(); + public boolean wasTouchingWater; +@@ -289,8 +_,8 @@ + public 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}; +@@ -342,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() { + } +@@ -526,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 entityType, Level level) { + this.type = entityType; + this.level = level; + this.dimensions = entityType.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; +@@ -908,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(io.papermc.paper.util.MCUtil.toLocation(this.level, this.level.getSharedSpawnPos())); else // Purpur - Add option to teleport to spawn on nether ceiling damage + this.onBelowWorld(); + } + } +@@ -1830,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(float fallDistance, float multiplier, DamageSource source) { +@@ -1899,7 +_,7 @@ + return this.isInWater() || flag; + } + +- public void updateInWaterStateAndDoWaterCurrentPushing() { ++ public void updateInWaterStateAndDoWaterCurrentPushing() { // Purpur - Movement options for armor stands - package-private -> public - TODO: use AT file + if (this.getVehicle() instanceof AbstractBoat abstractBoat && !abstractBoat.isUnderWater()) { + this.wasTouchingWater = false; + } else if (this.updateFluidHeightAndDoFluidPushing(FluidTags.WATER, 0.014)) { +@@ -2525,6 +_,13 @@ + compound.putBoolean("Paper.FreezeLock", true); + } + // Paper end ++ ++ // Purpur start - Fire immune API ++ if (immuneToFire != null) { ++ compound.putBoolean("Purpur.FireImmune", immuneToFire); ++ } ++ // Purpur end - Fire immune API ++ + return compound; + } catch (Throwable var9) { + CrashReport crashReport = CrashReport.forThrowable(var9, "Saving entity NBT"); +@@ -2674,6 +_,13 @@ + freezeLocked = compound.getBoolean("Paper.FreezeLock"); + } + // Paper end ++ ++ // Purpur start - Fire immune API ++ if (compound.contains("Purpur.FireImmune")) { ++ immuneToFire = compound.getBoolean("Purpur.FireImmune"); ++ } ++ // Purpur end - Fire immune API ++ + } catch (Throwable var17) { + CrashReport crashReport = CrashReport.forThrowable(var17, "Loading entity NBT"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being loaded"); +@@ -2920,6 +_,7 @@ + if (this.isAlive() && this instanceof Leashable leashable) { + if (leashable.getLeashHolder() == player) { + if (!this.level().isClientSide()) { ++ if (hand == InteractionHand.OFF_HAND && (level().purpurConfig.villagerCanBeLeashed || level().purpurConfig.wanderingTraderCanBeLeashed) && this instanceof net.minecraft.world.entity.npc.AbstractVillager) return InteractionResult.CONSUME; // Purpur - Allow leashing villagers + // CraftBukkit start - fire PlayerUnleashEntityEvent + // Paper start - Expand EntityUnleashEvent + org.bukkit.event.player.PlayerUnleashEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerUnleashEntityEvent(this, player, hand, !player.hasInfiniteMaterials()); +@@ -3245,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 + } + } + } +@@ -3458,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() { +@@ -3953,7 +_,7 @@ + // CraftBukkit end + + 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) { +@@ -4485,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()) { +@@ -4883,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 0000000000..ff1e039a1d --- /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 0000000000..6b937ec999 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/EntityType.java.patch @@ -0,0 +1,52 @@ +--- a/net/minecraft/world/entity/EntityType.java ++++ b/net/minecraft/world/entity/EntityType.java +@@ -1083,6 +_,16 @@ + return register(vanillaEntityId(key), builder); + } + ++ // Purpur start - PlayerSetSpawnerTypeWithEggEvent ++ public static EntityType getFromBukkitType(org.bukkit.entity.EntityType bukkitType) { ++ return getFromKey(ResourceLocation.parse(bukkitType.getKey().toString())); ++ } ++ ++ public static EntityType getFromKey(ResourceLocation location) { ++ return BuiltInRegistries.ENTITY_TYPE.getValue(location); ++ } ++ // Purpur end - PlayerSetSpawnerTypeWithEggEvent ++ + public static ResourceLocation getKey(EntityType entityType) { + return BuiltInRegistries.ENTITY_TYPE.getKey(entityType); + } +@@ -1312,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; + } +@@ -1370,7 +_,14 @@ + entity.load(tag); + }, + // Paper end - Don't fire sync event during generation +- () -> LOGGER.warn("Skipping Entity with id {}", tag.getString("id")) ++ // Purpur start - log skipped entity's position ++ () -> {LOGGER.warn("Skipping Entity with id {}", tag.getString("id")); ++ try { ++ ListTag pos = tag.getList("Pos", 6); ++ EntityType.LOGGER.warn("Location: {} {},{},{}", level.getWorld().getName(), pos.getDouble(0), pos.getDouble(1), pos.getDouble(2)); ++ } catch (Throwable ignore) {} ++ } ++ // Purpur end - log skipped entity's position + ); + } + 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 0000000000..a14e7f6a51 --- /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 +@@ -323,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 = CraftEventFactory.callPlayerXpCooldownEvent(entity, 2, PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entityhuman.takeXpDelay = 2; ++ entity.takeXpDelay = CraftEventFactory.callPlayerXpCooldownEvent(entity, this.level().purpurConfig.playerExpPickupDelay, PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entityhuman.takeXpDelay = 2; // Purpur - Configurable player pickup exp delay + entity.take(this, 1); + int i = this.repairPlayerItems(serverPlayer, this.value); + if (i > 0) { +@@ -339,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/GlowSquid.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/GlowSquid.java.patch new file mode 100644 index 0000000000..61c07231e8 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/GlowSquid.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/world/entity/GlowSquid.java ++++ b/net/minecraft/world/entity/GlowSquid.java +@@ -25,6 +_,13 @@ + super(entityType, 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/LivingEntity.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch new file mode 100644 index 0000000000..bf89dba4b5 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch @@ -0,0 +1,177 @@ +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -459,6 +_,12 @@ + if (d < 0.0) { + double damagePerBlock = this.level().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(io.papermc.paper.util.MCUtil.toLocation(this.level(), this.level().getSharedSpawnPos())); ++ 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))); + } + } +@@ -472,7 +_,7 @@ + && (!flag || !((Player)this).getAbilities().invulnerable); + if (flag1) { + this.setAirSupply(this.decreaseAirSupply(this.getAirSupply())); +- if (this.getAirSupply() == -20) { ++ if (this.getAirSupply() == -this.level().purpurConfig.drowningDamageInterval) { // Purpur - Drowning Settings + this.setAirSupply(0); + Vec3 deltaMovement = this.getDeltaMovement(); + +@@ -492,7 +_,7 @@ + ); + } + +- this.hurt(this.damageSources().drown(), 2.0F); ++ this.hurt(this.damageSources().drown(), (float) this.level().purpurConfig.damageFromDrowning); // Purpur - Drowning Settings + } + } else if (this.getAirSupply() < this.getMaxAirSupply()) { + this.setAirSupply(this.increaseAirSupply(this.getAirSupply())); +@@ -1009,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; + } +@@ -1063,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; +@@ -1372,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; +@@ -1536,11 +_,11 @@ + protected Player resolvePlayerResponsibleForDamage(DamageSource damageSource) { + Entity entity = damageSource.getEntity(); + if (entity instanceof Player player) { +- this.lastHurtByPlayerTime = 100; ++ this.lastHurtByPlayerTime = this.level().purpurConfig.mobLastHurtByPlayerTime; // Purpur - Config for mob last hurt by player time + this.lastHurtByPlayer = player; + return player; + } else if (entity instanceof Wolf wolf && wolf.isTame()) { +- this.lastHurtByPlayerTime = 100; ++ this.lastHurtByPlayerTime = this.level().purpurConfig.mobLastHurtByPlayerTime; // Purpur - Config for mob last hurt by player time + if (wolf.getOwner() instanceof Player player1) { + this.lastHurtByPlayer = player1; + } else { +@@ -1594,6 +_,18 @@ + } + } + ++ // 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().items) { ++ 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); +@@ -1790,6 +_,7 @@ + boolean flag = this.lastHurtByPlayerTime > 0; + this.dropEquipment(level); // CraftBukkit - from below + if (this.shouldDropLoot() && level.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { ++ 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; +@@ -1798,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 +@@ -2989,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); + } + } +@@ -4398,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 0000000000..9180322624 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/Mob.java.patch @@ -0,0 +1,84 @@ +--- a/net/minecraft/world/entity/Mob.java ++++ b/net/minecraft/world/entity/Mob.java +@@ -145,6 +_,7 @@ + private BlockPos restrictCenter = BlockPos.ZERO; + private float restrictRadius = -1.0F; + public boolean aware = true; // CraftBukkit ++ public int ticksSinceLastInteraction; // Purpur - Entity lifespan + + protected Mob(EntityType entityType, Level level) { + super(entityType, level); +@@ -294,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 +@@ -337,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 source) { +@@ -486,6 +_,7 @@ + compound.putBoolean("NoAI", this.isNoAi()); + } + compound.putBoolean("Bukkit.Aware", this.aware); // CraftBukkit ++ compound.putInt("Purpur.ticksSinceLastInteraction", this.ticksSinceLastInteraction); // Purpur - Entity lifespan + } + + @Override +@@ -568,6 +_,11 @@ + this.aware = compound.getBoolean("Bukkit.Aware"); + } + // CraftBukkit end ++ // Purpur start - Entity lifespan ++ if (compound.contains("Purpur.ticksSinceLastInteraction")) { ++ this.ticksSinceLastInteraction = compound.getInt("Purpur.ticksSinceLastInteraction"); ++ } ++ // Purpur end - Entity lifespan + } + + @Override +@@ -1280,7 +_,7 @@ + ); + } + +- this.setLeftHanded(random.nextFloat() < 0.05F); ++ this.setLeftHanded(random.nextFloat() < level.getLevel().purpurConfig.entityLeftHandedChance); // Purpur - Changeable Mob Left Handed Chance + return spawnGroupData; + } + +@@ -1619,6 +_,7 @@ + this.playAttackSound(); + } + ++ 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 0000000000..4cd99d012b --- /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 0000000000..c457ae742c --- /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 +@@ -86,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 0000000000..f90e63ebd3 --- /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 0000000000..2bbc2776e5 --- /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 +@@ -46,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/goal/LlamaFollowCaravanGoal.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/LlamaFollowCaravanGoal.java.patch new file mode 100644 index 0000000000..6fbb7bf98b --- /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 0000000000..f623c03a98 --- /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 0000000000..f321637d72 --- /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 +@@ -58,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 0000000000..b072c14f4b --- /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 +@@ -55,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 0000000000..7e2366a32e --- /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 0000000000..df5fd519b6 --- /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 +@@ -64,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/ambient/Bat.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ambient/Bat.java.patch new file mode 100644 index 0000000000..fc1ce3bb3d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/ambient/Bat.java.patch @@ -0,0 +1,23 @@ +--- a/net/minecraft/world/entity/ambient/Bat.java ++++ b/net/minecraft/world/entity/ambient/Bat.java +@@ -231,7 +_,7 @@ + } else { + int maxLocalRawBrightness = level.getMaxLocalRawBrightness(pos); + int i = 4; +- if (isHalloween()) { ++ if (Bat.isHalloweenSeason(level.getMinecraftWorld())) { // Purpur - Halloween options and optimizations + i = 7; + } else if (randomSource.nextBoolean()) { + return false; +@@ -243,6 +_,11 @@ + } + } + ++ // 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; ++ public static boolean isHalloweenSeason(Level level) { return level.purpurConfig.forceHalloweenSeason || isHalloween(); } // Purpur - Halloween options and optimizations + private static boolean isHalloween() { + LocalDate localDate = LocalDate.now(); + int i = localDate.get(ChronoField.DAY_OF_MONTH); 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 0000000000..56a99c4361 --- /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 +@@ -142,7 +_,7 @@ + ItemStack itemInHand = player.getItemInHand(hand); + if (this.isFood(itemInHand)) { + int age = this.getAge(); +- if (!this.level().isClientSide && age == 0 && this.canFallInLove()) { ++ if (!this.level().isClientSide && 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(player, breedCopy); // Paper - Fix EntityBreedEvent copying +@@ -239,10 +_,20 @@ + public void spawnChildFromBreeding(ServerLevel level, Animal mate) { + AgeableMob breedOffspring = this.getBreedOffspring(level, mate); + if (breedOffspring != null) { +- breedOffspring.setBaby(true); +- breedOffspring.moveTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F); ++ //breedOffspring.setBaby(true); // Purpur - Add adjustable breeding cooldown to config - moved down ++ //breedOffspring.moveTo(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(mate.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.moveTo(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, mate, breeder, this.breedItem, experience); + if (entityBreedEvent.isCancelled()) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Bee.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Bee.java.patch new file mode 100644 index 0000000000..b25618ac32 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Bee.java.patch @@ -0,0 +1,43 @@ +--- a/net/minecraft/world/entity/animal/Bee.java ++++ b/net/minecraft/world/entity/animal/Bee.java +@@ -365,7 +_,7 @@ + } + + public static boolean isNightOrRaining(Level level) { +- return level.dimensionType().hasSkyLight() && (level.isNight() || level.isRaining()); ++ return level.dimensionType().hasSkyLight() && (level.isNight() && !level.purpurConfig.beeCanWorkAtNight || level.isRaining() && !level.purpurConfig.beeCanWorkInRain); // Purpur - Bee can work when raining or at night + } + + public void setStayOutOfHiveCountdown(int stayOutOfHiveCountdown) { +@@ -398,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) { +@@ -1136,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(), io.papermc.paper.util.MCUtil.toLocation(Bee.this.level(), Bee.this.savedFlowerPos)).callEvent(); // Purpur - Bee API + return true; + } else { + Bee.this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(Bee.this.random, 20, 60); +@@ -1182,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 : io.papermc.paper.util.MCUtil.toLocation(Bee.this.level(), Bee.this.savedFlowerPos), Bee.this.hasNectar()).callEvent(); // Purpur - Bee API + } + + @Override +@@ -1228,6 +_,7 @@ + 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 - 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/Cat.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Cat.java.patch new file mode 100644 index 0000000000..e2dcf1d105 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Cat.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/world/entity/animal/Cat.java ++++ b/net/minecraft/world/entity/animal/Cat.java +@@ -332,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 ++ + @Nullable + @Override + public SpawnGroupData finalizeSpawn( +@@ -438,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, (byte)7); diff --git a/patches/server/0033-Cows-eat-mushrooms.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Cow.java.patch similarity index 52% rename from patches/server/0033-Cows-eat-mushrooms.patch rename to purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Cow.java.patch index e147368338..61b3e99834 100644 --- a/patches/server/0033-Cows-eat-mushrooms.patch +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Cow.java.patch @@ -1,39 +1,31 @@ -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 064188a7032170ed16cf3b538efc444e54325036..88dd147317e200a11eb6a7e496bfffee9079fd6f 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cow.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java -@@ -75,7 +75,7 @@ public class Cow extends Animal { - 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, (itemstack) -> { -- return itemstack.is(ItemTags.COW_FOOD); -+ return 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); // Purpur - Cows eat mushrooms - }, false)); - this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25D)); - this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D)); -@@ -137,6 +137,10 @@ public class Cow extends Animal { - - player.setItemInHand(hand, itemstack1); +--- a/net/minecraft/world/entity/animal/Cow.java ++++ b/net/minecraft/world/entity/animal/Cow.java +@@ -43,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)); +@@ -99,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 - feed mushroom to change to mooshroom - Cows eat mushrooms -+ } else if (level().purpurConfig.cowFeedMushrooms > 0 && this.getType() != EntityType.MOOSHROOM && isMushroom(itemstack)) { -+ return this.feedMushroom(player, itemstack); ++ // 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); } -@@ -152,4 +156,67 @@ public class Cow extends Animal { +@@ -114,4 +_,67 @@ public EntityDimensions getDefaultDimensions(Pose pose) { - return this.isBaby() ? Cow.BABY_DIMENSIONS : super.getDefaultDimensions(pose); + return this.isBaby() ? BABY_DIMENSIONS : super.getDefaultDimensions(pose); } + -+ // Purpur start - feed mushroom to change to mooshroom - Cows eat mushrooms ++ // Purpur start - Cows eat mushrooms - feed mushroom to change to mooshroom + private int redMushroomsFed = 0; + private int brownMushroomsFed = 0; + @@ -78,7 +70,7 @@ index 064188a7032170ed16cf3b538efc444e54325036..88dd147317e200a11eb6a7e496bfffee + if (this.hasCustomName()) { + mooshroom.setCustomName(this.getCustomName()); + } -+ if (CraftEventFactory.callEntityTransformEvent(this, mooshroom, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTransformEvent(this, mooshroom, org.bukkit.event.entity.EntityTransformEvent.TransformReason.INFECTION).isCancelled()) { + return InteractionResult.PASS; + } + this.level().addFreshEntity(mooshroom); @@ -96,23 +88,3 @@ index 064188a7032170ed16cf3b538efc444e54325036..88dd147317e200a11eb6a7e496bfffee + } + // Purpur end - Cows eat mushrooms } -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index b3284dfbcdc240919776f47a780a824c406c2781..6eb25a9b4bb634e57f6de015c9a9f2fc78d0b86f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -390,6 +390,7 @@ public class PurpurWorldConfig { - public boolean cowControllable = true; - public double cowMaxHealth = 10.0D; - public double cowScale = 1.0D; -+ public int cowFeedMushrooms = 0; - private void cowSettings() { - cowRidable = getBoolean("mobs.cow.ridable", cowRidable); - cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater); -@@ -401,6 +402,7 @@ public class PurpurWorldConfig { - } - 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); - } - - public boolean creakingRidable = false; diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Dolphin.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Dolphin.java.patch new file mode 100644 index 0000000000..7a3ebe2c4d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Dolphin.java.patch @@ -0,0 +1,47 @@ +--- a/net/minecraft/world/entity/animal/Dolphin.java ++++ b/net/minecraft/world/entity/animal/Dolphin.java +@@ -71,6 +_,7 @@ + private static final int TOTAL_MOISTNESS_LEVEL = 2400; + public static final Predicate ALLOWED_ITEMS = itemEntity -> !itemEntity.hasPickUpDelay() && itemEntity.isAlive() && itemEntity.isInWater(); + public static final float BABY_SCALE = 0.65F; ++ private boolean isNaturallyAggressiveToPlayers; // Purpur - Dolphins naturally aggressive to players chance + + public Dolphin(EntityType entityType, Level level) { + super(entityType, level); +@@ -87,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); + } + +@@ -169,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() { +@@ -412,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/Fox.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Fox.java.patch new file mode 100644 index 0000000000..c0f401ee5c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Fox.java.patch @@ -0,0 +1,52 @@ +--- a/net/minecraft/world/entity/animal/Fox.java ++++ b/net/minecraft/world/entity/animal/Fox.java +@@ -335,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); +@@ -364,6 +_,7 @@ + @Override + 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 + } + + List getTrustedUUIDs() { +@@ -684,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/IronGolem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/IronGolem.java.patch new file mode 100644 index 0000000000..176e8f9bcc --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/IronGolem.java.patch @@ -0,0 +1,53 @@ +--- a/net/minecraft/world/entity/animal/IronGolem.java ++++ b/net/minecraft/world/entity/animal/IronGolem.java +@@ -56,13 +_,26 @@ + private int remainingPersistentAngerTime; + @Nullable + private UUID persistentAngerTarget; ++ @Nullable private UUID summoner; // Purpur - Summoner API + + public IronGolem(EntityType entityType, Level level) { + super(entityType, level); + } + ++ // Purpur start - Summoner API ++ @Nullable ++ public UUID getSummoner() { ++ return summoner; ++ } ++ ++ public void setSummoner(@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)); +@@ -140,6 +_,7 @@ + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); + compound.putBoolean("PlayerCreated", this.isPlayerCreated()); ++ if (getSummoner() != null) compound.putUUID("Purpur.Summoner", getSummoner()); // Purpur - Summoner API + this.addPersistentAngerSaveData(compound); + } + +@@ -147,6 +_,7 @@ + public void readAdditionalSaveData(CompoundTag compound) { + super.readAdditionalSaveData(compound); + this.setPlayerCreated(compound.getBoolean("PlayerCreated")); ++ if (compound.contains("Purpur.Summoner")) setSummoner(compound.getUUID("Purpur.Summoner")); // Purpur - Summoner API + this.readPersistentAngerSaveData(this.level(), compound); + } + +@@ -266,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/MushroomCow.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/MushroomCow.java.patch new file mode 100644 index 0000000000..1d66518654 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/MushroomCow.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/world/entity/animal/MushroomCow.java ++++ b/net/minecraft/world/entity/animal/MushroomCow.java +@@ -192,6 +_,13 @@ + level.playSound(null, this, SoundEvents.MOOSHROOM_SHEAR, soundSource, 1.0F, 1.0F); + this.convertTo(EntityType.COW, ConversionParams.single(this, false, false), mob -> { + 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 ++ mob.copyPosition(this); ++ mob.yBodyRot = this.yBodyRot; ++ mob.setYHeadRot(this.getYHeadRot()); ++ mob.yRotO = this.yRotO; ++ mob.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/Ocelot.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Ocelot.java.patch new file mode 100644 index 0000000000..419638b78a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Ocelot.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/animal/Ocelot.java ++++ b/net/minecraft/world/entity/animal/Ocelot.java +@@ -232,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/Parrot.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Parrot.java.patch new file mode 100644 index 0000000000..04548f96b5 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Parrot.java.patch @@ -0,0 +1,52 @@ +--- a/net/minecraft/world/entity/animal/Parrot.java ++++ b/net/minecraft/world/entity/animal/Parrot.java +@@ -152,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)); +@@ -257,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, (byte)7); + } else { +@@ -265,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)) { +@@ -289,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( +@@ -304,13 +_,13 @@ + + @Override + public boolean canMate(Animal otherAnimal) { +- return false; ++ return super.canMate(otherAnimal); // Purpur - Breedable parrots + } + + @Nullable + @Override + public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob otherParent) { +- return null; ++ return level.purpurConfig.parrotBreedable ? EntityType.PARROT.create(level, EntitySpawnReason.BREEDING) : null; // Purpur - Breedable parrots + } + + @Nullable diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Pig.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Pig.java.patch new file mode 100644 index 0000000000..7ae89deb5d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Pig.java.patch @@ -0,0 +1,22 @@ +--- a/net/minecraft/world/entity/animal/Pig.java ++++ b/net/minecraft/world/entity/animal/Pig.java +@@ -132,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.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 - 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.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/PolarBear.java.patch new file mode 100644 index 0000000000..775a03fbe7 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/PolarBear.java.patch @@ -0,0 +1,54 @@ +--- a/net/minecraft/world/entity/animal/PolarBear.java ++++ b/net/minecraft/world/entity/animal/PolarBear.java +@@ -64,6 +_,29 @@ + super(entityType, 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 ++ + @Nullable + @Override + public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob otherParent) { +@@ -72,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 +@@ -81,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.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Rabbit.java.patch new file mode 100644 index 0000000000..ed0b1c6aa3 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Rabbit.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/world/entity/animal/Rabbit.java ++++ b/net/minecraft/world/entity/animal/Rabbit.java +@@ -376,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/SnowGolem.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/SnowGolem.java.patch new file mode 100644 index 0000000000..b6401d7642 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/SnowGolem.java.patch @@ -0,0 +1,63 @@ +--- a/net/minecraft/world/entity/animal/SnowGolem.java ++++ b/net/minecraft/world/entity/animal/SnowGolem.java +@@ -44,15 +_,27 @@ + public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackMob { + private static final EntityDataAccessor DATA_PUMPKIN_ID = SynchedEntityData.defineId(SnowGolem.class, EntityDataSerializers.BYTE); + private static final byte PUMPKIN_FLAG = 16; ++ @Nullable private java.util.UUID summoner; // Purpur - Summoner API + + public SnowGolem(EntityType entityType, Level level) { + super(entityType, level); + } + ++ // Purpur start - Summoner API ++ @Nullable ++ public java.util.UUID getSummoner() { ++ return summoner; ++ } ++ ++ public void setSummoner(@Nullable java.util.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)); +@@ -72,6 +_,7 @@ + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); + compound.putBoolean("Pumpkin", this.hasPumpkin()); ++ if (getSummoner() != null) compound.putUUID("Purpur.Summoner", getSummoner()); // Purpur - Summoner API + } + + @Override +@@ -80,6 +_,7 @@ + if (compound.contains("Pumpkin")) { + this.setPumpkin(compound.getBoolean("Pumpkin")); + } ++ if (compound.contains("Purpur.Summoner")) setSummoner(compound.getUUID("Purpur.Summoner")); // 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/Squid.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Squid.java.patch new file mode 100644 index 0000000000..af92a24ddc --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Squid.java.patch @@ -0,0 +1,50 @@ +--- a/net/minecraft/world/entity/animal/Squid.java ++++ b/net/minecraft/world/entity/animal/Squid.java +@@ -46,10 +_,29 @@ + + public Squid(EntityType entityType, Level level) { + super(entityType, 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)); +@@ -127,6 +_,7 @@ + } + + if (this.isInWaterOrBubble()) { ++ 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; +@@ -310,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/WaterAnimal.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/WaterAnimal.java.patch new file mode 100644 index 0000000000..b42850fc85 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/WaterAnimal.java.patch @@ -0,0 +1,12 @@ +--- a/net/minecraft/world/entity/animal/WaterAnimal.java ++++ b/net/minecraft/world/entity/animal/WaterAnimal.java +@@ -74,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/patches/server/0108-Configurable-chance-for-wolves-to-spawn-rabid.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Wolf.java.patch similarity index 52% rename from patches/server/0108-Configurable-chance-for-wolves-to-spawn-rabid.patch rename to purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Wolf.java.patch index a248f7e107..4c48e990c4 100644 --- a/patches/server/0108-Configurable-chance-for-wolves-to-spawn-rabid.patch +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Wolf.java.patch @@ -1,18 +1,8 @@ -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 539eba7148be12eb05c907ed86b0cea975424874..966fe4544212cc831ae617bc2eb05189102ff470 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java -@@ -103,6 +103,37 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder 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; @@ -48,10 +38,10 @@ index 539eba7148be12eb05c907ed86b0cea975424874..966fe4544212cc831ae617bc2eb05189 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; -@@ -159,6 +190,31 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder(this, Llama.class, 24.0F, 1.5D, 1.5D)); + 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.0D, true)); - this.goalSelector.addGoal(6, new FollowOwnerGoal(this, 1.0D, 10.0F, 2.0F)); -@@ -179,7 +236,7 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder(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(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)); -@@ -228,6 +285,7 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder { - nbt.putString("variant", resourcekey.location().toString()); - }); -@@ -245,6 +303,10 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder compound.putString("variant", resourceKey.location().toString())); + this.addPersistentAngerSaveData(compound); + } +@@ -196,6 +_,10 @@ + if (compound.contains("CollarColor", 99)) { + this.setCollarColor(DyeColor.byId(compound.getInt("CollarColor"))); } + // Purpur start - Configurable chance for wolves to spawn rabid -+ this.isRabid = nbt.getBoolean("Purpur.IsRabid"); ++ this.isRabid = compound.getBoolean("Purpur.IsRabid"); + this.updatePathfinders(false); + // Purpur end - Configurable chance for wolves to spawn rabid - this.readPersistentAngerSaveData(this.level(), nbt); + this.readPersistentAngerSaveData(this.level(), compound); } -@@ -263,6 +325,10 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder 0.0D && random.nextDouble() <= world.getLevel().purpurConfig.wolfNaturalRabid; ++ 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(world, difficulty, spawnReason, (SpawnGroupData) entityData); + return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData); } -@@ -306,6 +372,11 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder 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/horse/Llama.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/horse/Llama.java.patch new file mode 100644 index 0000000000..c0e613a836 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/animal/horse/Llama.java.patch @@ -0,0 +1,42 @@ +--- a/net/minecraft/world/entity/animal/horse/Llama.java ++++ b/net/minecraft/world/entity/animal/horse/Llama.java +@@ -72,6 +_,7 @@ + private Llama caravanHead; + @Nullable + public Llama caravanTail; // Paper ++ public boolean shouldJoinCaravan = true; // Purpur - Llama API + + public Llama(EntityType entityType, Level level) { + super(entityType, level); +@@ -106,6 +_,7 @@ + super.addAdditionalSaveData(compound); + compound.putInt("Variant", this.getVariant().id); + compound.putInt("Strength", this.getStrength()); ++ compound.putBoolean("Purpur.ShouldJoinCaravan", shouldJoinCaravan); // Purpur - Llama API + } + + @Override +@@ -113,6 +_,7 @@ + this.setStrength(compound.getInt("Strength")); + super.readAdditionalSaveData(compound); + this.setVariant(Llama.Variant.byId(compound.getInt("Variant"))); ++ if (compound.contains("Purpur.ShouldJoinCaravan")) this.shouldJoinCaravan = compound.getBoolean("Purpur.ShouldJoinCaravan"); // Purpur - Llama API + } + + @Override +@@ -386,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; + } + +@@ -393,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/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 0000000000..5deaa74ba2 --- /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 +@@ -38,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; +@@ -74,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 +@@ -119,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 0000000000..e07733ccfb --- /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 +@@ -974,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; + } + +@@ -1009,7 +_,7 @@ + boolean flag = worldserver.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT); + 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 0000000000..30e1ffb251 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/boss/wither/WitherBoss.java.patch @@ -0,0 +1,74 @@ +--- a/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -77,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); ++ @Nullable private java.util.UUID summoner; // Purpur - Summoner API + + public WitherBoss(EntityType entityType, Level level) { + super(entityType, level); +@@ -85,6 +_,17 @@ + this.xpReward = 50; + } + ++ // Purpur start - Summoner API ++ @Nullable ++ public java.util.UUID getSummoner() { ++ return summoner; ++ } ++ ++ public void setSummoner(@Nullable java.util.UUID summoner) { ++ this.summoner = summoner; ++ } ++ // Purpur end - Summoner API ++ + @Override + protected PathNavigation createNavigation(Level level) { + FlyingPathNavigation flyingPathNavigation = new FlyingPathNavigation(this, level); +@@ -117,6 +_,7 @@ + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); + compound.putInt("Invul", this.getInvulnerableTicks()); ++ if (getSummoner() != null) compound.putUUID("Purpur.Summoner", getSummoner()); // Purpur - Summoner API + } + + @Override +@@ -126,6 +_,7 @@ + if (this.hasCustomName()) { + this.bossEvent.setName(this.getDisplayName()); + } ++ if (compound.contains("Purpur.Summoner")) setSummoner(compound.getUUID("Purpur.Summoner")); // Purpur - Summoner API + } + + @Override +@@ -269,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; +@@ -376,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()); +@@ -574,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 0000000000..1ac4c29b0f --- /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 +@@ -93,10 +_,13 @@ + private boolean noTickPoseDirty = 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 entityType, Level level) { + super(entityType, 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) { +@@ -620,6 +_,7 @@ + + @Override + public void tick() { ++ maxUpStep = level().purpurConfig.armorstandStepHeight; // Purpur - Add option to set armorstand step height + // Paper start - Allow ArmorStands not to tick + if (!this.canTick) { + if (this.noTickPoseDirty) { +@@ -949,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 0000000000..0244697994 --- /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 +@@ -52,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 entityType, Level level) { + super(entityType, level); +@@ -337,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().getBoolean(GameRules.RULE_MOBGRIEFING) && damageSource.getEntity() instanceof Mob) { + return false; +@@ -539,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 0000000000..eb0ed35f33 --- /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 +@@ -251,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, LivingEntity.getSlotForHand(hand)); ++ 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/AbstractSkeleton.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/AbstractSkeleton.java.patch new file mode 100644 index 0000000000..37349cc6c6 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/AbstractSkeleton.java.patch @@ -0,0 +1,23 @@ +--- a/net/minecraft/world/entity/monster/AbstractSkeleton.java ++++ b/net/minecraft/world/entity/monster/AbstractSkeleton.java +@@ -158,10 +_,7 @@ + this.reassessWeaponGoal(); + this.setCanPickUpLoot(this.level().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()) { +- LocalDate localDate = LocalDate.now(); +- int i = localDate.get(ChronoField.DAY_OF_MONTH); +- int i1 = localDate.get(ChronoField.MONTH_OF_YEAR); +- if (i1 == 10 && i == 31 && random.nextFloat() < 0.25F) { ++ if (net.minecraft.world.entity.ambient.Bat.isHalloweenSeason(level.getMinecraftWorld()) && this.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.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F; + } +@@ -217,7 +_,7 @@ + if (event.getProjectile() == arrow.getBukkitEntity()) { + // CraftBukkit end + Projectile.spawnProjectileUsingShoot( +- 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 + ); + } // CraftBukkit + } 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 0000000000..c3719371cc --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Creeper.java.patch @@ -0,0 +1,64 @@ +--- a/net/minecraft/world/entity/monster/Creeper.java ++++ b/net/minecraft/world/entity/monster/Creeper.java +@@ -50,6 +_,7 @@ + public int explosionRadius = 3; + private int droppedSkulls; + public Entity entityIgniter; // CraftBukkit ++ private boolean exploding = false; // Purpur - Config to make Creepers explode on death + + public Creeper(EntityType entityType, Level level) { + super(entityType, level); +@@ -161,6 +_,26 @@ + } + } + ++ // Purpur start - Special mobs naturally spawn ++ public net.minecraft.world.entity.SpawnGroupData finalizeSpawn(net.minecraft.world.level.ServerLevelAccessor world, net.minecraft.world.DifficultyInstance difficulty, net.minecraft.world.entity.EntitySpawnReason spawnReason, @Nullable net.minecraft.world.entity.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 + protected 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().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING) && 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/Drowned.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Drowned.java.patch new file mode 100644 index 0000000000..3d8569ee4e --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Drowned.java.patch @@ -0,0 +1,27 @@ +--- a/net/minecraft/world/entity/monster/Drowned.java ++++ b/net/minecraft/world/entity/monster/Drowned.java +@@ -82,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/EnderMan.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/EnderMan.java.patch new file mode 100644 index 0000000000..bac97afe77 --- /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 +@@ -102,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)); + } + +@@ -230,7 +_,7 @@ + + boolean isBeingStaredBy(Player player) { + // Paper start - EndermanAttackPlayerEvent +- final boolean shouldAttack = isBeingStaredBy0(player); ++ final boolean shouldAttack = !this.level().purpurConfig.endermanDisableStareAggro && 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(); +@@ -385,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 { + boolean flag = damageSource.getDirectEntity() instanceof ThrownPotion; + if (!damageSource.is(DamageTypeTags.IS_PROJECTILE) && !flag) { +@@ -397,6 +_,7 @@ + } else { + boolean flag1 = flag && this.hurtWithCleanWater(level, damageSource, (ThrownPotion)damageSource.getDirectEntity(), amount); + ++ if (!flag1 && 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()) { +@@ -440,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 { +@@ -484,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().getBoolean(GameRules.RULE_MOBGRIEFING) + && this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0; +@@ -633,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().getBoolean(GameRules.RULE_MOBGRIEFING) + && 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 0000000000..9965430e88 --- /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 +@@ -28,12 +_,23 @@ + public class Endermite extends Monster { + private static final int MAX_LIFE = 2400; + public int life; ++ private boolean isPlayerSpawned; // Purpur - Add back player spawned endermite API + + public Endermite(EntityType entityType, Level level) { + super(entityType, 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)); +@@ -79,12 +_,14 @@ + public void readAdditionalSaveData(CompoundTag compound) { + super.readAdditionalSaveData(compound); + this.life = compound.getInt("Lifetime"); ++ this.isPlayerSpawned = compound.getBoolean("PlayerSpawned"); // Purpur - Add back player spawned endermite API + } + + @Override + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); + compound.putInt("Lifetime", this.life); ++ compound.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/Monster.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Monster.java.patch new file mode 100644 index 0000000000..c833d4fc6f --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Monster.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/world/entity/monster/Monster.java ++++ b/net/minecraft/world/entity/monster/Monster.java +@@ -88,6 +_,14 @@ + } + + public static boolean isDarkEnoughToSpawn(ServerLevelAccessor level, BlockPos pos, RandomSource random) { ++ // Purpur start - Config to disable hostile mob spawn on ice ++ if (!level.getMinecraftWorld().purpurConfig.mobsSpawnOnPackedIce || !level.getMinecraftWorld().purpurConfig.mobsSpawnOnBlueIce) { ++ net.minecraft.world.level.block.state.BlockState spawnBlock = level.getBlockState(pos.below()); ++ if ((!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))) { ++ return false; ++ } ++ } ++ // Purpur end - Config to disable hostile mob spawn on ice + if (level.getBrightness(LightLayer.SKY, pos) > random.nextInt(32)) { + return false; + } else { 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 0000000000..acec891216 --- /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 +@@ -158,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 0000000000..6e2afb3922 --- /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 +@@ -70,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.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)); +@@ -150,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 0000000000..eee30bbe50 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Shulker.java.patch @@ -0,0 +1,67 @@ +--- a/net/minecraft/world/entity/monster/Shulker.java ++++ b/net/minecraft/world/entity/monster/Shulker.java +@@ -50,6 +_,7 @@ + import net.minecraft.world.level.ServerLevelAccessor; + import net.minecraft.world.level.block.Blocks; + import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.entity.EntityTypeTest; + import net.minecraft.world.level.gameevent.GameEvent; + import net.minecraft.world.phys.AABB; + import net.minecraft.world.phys.Vec3; +@@ -88,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)); +@@ -459,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((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.moveTo(vec3); +@@ -573,7 +_,7 @@ + + @Override + 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 + } + + @Nullable diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Skeleton.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Skeleton.java.patch new file mode 100644 index 0000000000..bba419b186 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Skeleton.java.patch @@ -0,0 +1,67 @@ +--- a/net/minecraft/world/entity/monster/Skeleton.java ++++ b/net/minecraft/world/entity/monster/Skeleton.java +@@ -135,4 +_,64 @@ + this.spawnAtLocation(level, Items.SKELETON_SKULL); + } + } ++ ++ // 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.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 (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) { ++ ((ServerLevel) level()).sendParticlesSource(((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/Strider.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Strider.java.patch new file mode 100644 index 0000000000..efb04f5ac0 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Strider.java.patch @@ -0,0 +1,22 @@ +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -414,6 +_,19 @@ + @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.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 (!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/Vindicator.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Vindicator.java.patch new file mode 100644 index 0000000000..fe07bf8095 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Vindicator.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/monster/Vindicator.java ++++ b/net/minecraft/world/entity/monster/Vindicator.java +@@ -132,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/Zombie.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Zombie.java.patch new file mode 100644 index 0000000000..ba29551efd --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/Zombie.java.patch @@ -0,0 +1,35 @@ +--- a/net/minecraft/world/entity/monster/Zombie.java ++++ b/net/minecraft/world/entity/monster/Zombie.java +@@ -114,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)); + } +@@ -550,10 +_,7 @@ + } + + if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { +- LocalDate localDate = LocalDate.now(); +- int i = localDate.get(ChronoField.DAY_OF_MONTH); +- int i1 = localDate.get(ChronoField.MONTH_OF_YEAR); +- if (i1 == 10 && i == 31 && random.nextFloat() < 0.25F) { ++ if (net.minecraft.world.entity.ambient.Bat.isHalloweenSeason(level.getMinecraftWorld()) && this.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.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F; + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombieVillager.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombieVillager.java.patch new file mode 100644 index 0000000000..e5517a82e0 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombieVillager.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/world/entity/monster/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/ZombieVillager.java +@@ -156,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/ZombifiedPiglin.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombifiedPiglin.java.patch new file mode 100644 index 0000000000..e1a87265fa --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombifiedPiglin.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/entity/monster/ZombifiedPiglin.java ++++ b/net/minecraft/world/entity/monster/ZombifiedPiglin.java +@@ -112,7 +_,7 @@ + this.maybeAlertOthers(); + } + +- if (this.isAngry()) { ++ if (this.isAngry() && this.level().purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) { // Purpur - Toggle for Zombified Piglin death always counting as player kill when angry + this.lastHurtByPlayerTime = this.tickCount; + } + +@@ -163,7 +_,7 @@ + this.ticksUntilNextAlert = ALERT_INTERVAL.sample(this.random); + } + +- if (livingEntity instanceof Player) { ++ if (livingEntity instanceof Player && this.level().purpurConfig.zombifiedPiglinCountAsPlayerKillWhenAngry) { // Purpur - Toggle for Zombified Piglin death always counting as player kill when angry + this.setLastHurtByPlayer((Player)livingEntity); + } + 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 0000000000..c7ccb700da --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch @@ -0,0 +1,32 @@ +--- a/net/minecraft/world/entity/monster/piglin/PiglinAi.java ++++ b/net/minecraft/world/entity/monster/piglin/PiglinAi.java +@@ -4,6 +_,7 @@ + import com.google.common.collect.ImmutableSet; + import com.mojang.datafixers.util.Pair; + import java.util.Collections; ++import java.util.Iterator; + import java.util.List; + import java.util.Optional; + import net.minecraft.server.level.ServerLevel; +@@ -666,13 +_,20 @@ + + public static boolean isWearingSafeArmor(LivingEntity entity) { + for (ItemStack itemStack : entity.getArmorAndBodyArmorSlots()) { +- if (itemStack.is(ItemTags.PIGLIN_SAFE_ARMOR)) { ++ if (itemStack.is(ItemTags.PIGLIN_SAFE_ARMOR) || (entity.level().purpurConfig.piglinIgnoresArmorWithGoldTrim && isWearingGoldTrim(itemStack.getItem()))) { // Purpur - piglins ignore gold-trimmed armor + return true; + } + } + + return false; + } ++ ++ // Purpur start - piglins ignore gold-trimmed armor ++ private static boolean isWearingGoldTrim(Item itemstack) { ++ net.minecraft.world.item.equipment.trim.ArmorTrim armorTrim = itemstack.components().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/npc/CatSpawner.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch new file mode 100644 index 0000000000..d5c8708665 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/CatSpawner.java.patch @@ -0,0 +1,39 @@ +--- a/net/minecraft/world/entity/npc/CatSpawner.java ++++ b/net/minecraft/world/entity/npc/CatSpawner.java +@@ -27,7 +_,7 @@ + if (this.nextTick > 0) { + return 0; + } else { +- this.nextTick = 1200; ++ this.nextTick = level.purpurConfig.catSpawnDelay; // Purpur - Cat spawning options + Player randomPlayer = level.getRandomPlayer(); + if (randomPlayer == null) { + return 0; +@@ -61,8 +_,12 @@ + + private int spawnInVillage(ServerLevel serverLevel, BlockPos pos) { + int i = 48; +- if (serverLevel.getPoiManager().getCountInRange(holder -> holder.is(PoiTypes.HOME), pos, 48, PoiManager.Occupancy.IS_OCCUPIED) > 4L) { +- List entitiesOfClass = serverLevel.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(48.0, 8.0, 48.0)); ++ // Purpur start - Cat spawning options ++ int range = serverLevel.purpurConfig.catSpawnVillageScanRange; ++ if (range <= 0) return 0; ++ if (serverLevel.getPoiManager().getCountInRange(holder -> holder.is(PoiTypes.HOME), pos, range, PoiManager.Occupancy.IS_OCCUPIED) > 4L) { ++ List entitiesOfClass = serverLevel.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(range, 8.0, range)); ++ // Purpur end - Cat spawning options + if (entitiesOfClass.size() < 5) { + return this.spawnCat(pos, serverLevel); + } +@@ -73,7 +_,11 @@ + + private int spawnInHut(ServerLevel serverLevel, BlockPos pos) { + int i = 16; +- List entitiesOfClass = serverLevel.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(16.0, 8.0, 16.0)); ++ // Purpur start - Cat spawning options ++ int range = serverLevel.purpurConfig.catSpawnSwampHutScanRange; ++ if (range <= 0) return 0; ++ List entitiesOfClass = serverLevel.getEntitiesOfClass(Cat.class, new AABB(pos).inflate(range, 8.0, range)); ++ // Purpur end - Cat spawning options + return entitiesOfClass.size() < 1 ? this.spawnCat(pos, serverLevel) : 0; + } + diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/Villager.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/Villager.java.patch new file mode 100644 index 0000000000..e203ed90fe --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/Villager.java.patch @@ -0,0 +1,142 @@ +--- a/net/minecraft/world/entity/npc/Villager.java ++++ b/net/minecraft/world/entity/npc/Villager.java +@@ -179,6 +_,8 @@ + MemoryModuleType.MEETING_POINT, + (villager, holder) -> holder.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 entityType, Level level) { + this(entityType, level, VillagerType.PLAINS); +@@ -193,6 +_,57 @@ + this.setVillagerData(this.getVillagerData().setType(villagerType).setProfession(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(); +@@ -289,11 +_,23 @@ + // 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; ++ } ++ // Purpur end - Lobotomize stuck villagers ++ // Pufferfish start ++ if (!inactive /*&& this.behaviorTick++ % this.activatedPriority == 0*/) { ++ this.getBrain().tick(level, this); // Paper - EAR 2 ++ } ++ // Pufferfish end + profilerFiller.pop(); + if (this.assignProfessionWhenSpawned) { + this.assignProfessionWhenSpawned = false; +@@ -365,6 +_,7 @@ + return InteractionResult.CONSUME; + } + ++ if (this.level().purpurConfig.villagerAllowTrading) // Purpur - Add config for villager trading + this.startTrading(player); + } + +@@ -503,7 +_,7 @@ + + private void updateDemand() { + for (MerchantOffer merchantOffer : this.getOffers()) { +- merchantOffer.updateDemand(); ++ merchantOffer.updateDemand(this.level().purpurConfig.villagerMinimumDemand); // Purpur - Configurable minimum demand for trades + } + } + +@@ -707,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() { +@@ -928,6 +_,7 @@ + } + + public void spawnGolemIfNeeded(ServerLevel serverLevel, long gameTime, int minVillagerAmount) { ++ if (serverLevel.purpurConfig.villagerSpawnIronGolemRadius > 0 && serverLevel.getEntitiesOfClass(net.minecraft.world.entity.animal.IronGolem.class, getBoundingBox().inflate(serverLevel.purpurConfig.villagerSpawnIronGolemRadius)).size() > serverLevel.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 = serverLevel.getEntitiesOfClass(Villager.class, aabb); +@@ -1001,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.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/WanderingTrader.java.patch new file mode 100644 index 0000000000..e1ca1ac526 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/WanderingTrader.java.patch @@ -0,0 +1,36 @@ +--- a/net/minecraft/world/entity/npc/WanderingTrader.java ++++ b/net/minecraft/world/entity/npc/WanderingTrader.java +@@ -69,6 +_,13 @@ + super(entityType, 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)); +@@ -89,7 +_,7 @@ + this, + new ItemStack(Items.MILK_BUCKET), + SoundEvents.WANDERING_TRADER_REAPPEARED, +- wanderingTrader -> this.canDrinkMilk && this.level().isDay() && wanderingTrader.isInvisible() // Paper - Add more WanderingTrader API ++ wanderingTrader -> level().purpurConfig.milkClearsBeneficialEffects && this.canDrinkMilk && this.level().isDay() && wanderingTrader.isInvisible() // Paper - Add more WanderingTrader API // // Purpur - Milk Keeps Beneficial Effects + ) + ); + this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this)); +@@ -133,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/WanderingTraderSpawner.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/WanderingTraderSpawner.java.patch new file mode 100644 index 0000000000..f1ac7fc154 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/npc/WanderingTraderSpawner.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/world/entity/npc/WanderingTraderSpawner.java ++++ b/net/minecraft/world/entity/npc/WanderingTraderSpawner.java +@@ -147,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 0000000000..9cd3b72ab6 --- /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 +@@ -200,11 +_,20 @@ + private int currentImpulseContextResetGraceTime; + 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(); +@@ -262,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); +@@ -340,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) { +@@ -610,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); +@@ -1269,7 +_,7 @@ + flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits + if (flag2) { + damageSource = damageSource.critical(true); // Paper start - critical damage API +- f *= 1.5F; ++ f *= this.level().purpurConfig.playerCriticalDamageMultiplier; // Purpur - Add config change multiplier critical damage value + } + + float f2 = f + f1; +@@ -1882,7 +_,23 @@ + + @Override + protected int getBaseExperienceReward(ServerLevel level) { +- return !level.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !this.isSpectator() ? Math.min(this.experienceLevel * 7, 100) : 0; ++ // Purpur start - Add player death exp control options ++ if (!level.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !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 +@@ -1965,6 +_,13 @@ + public boolean canUseSlot(EquipmentSlot slot) { + return slot != EquipmentSlot.BODY; + } ++ ++ // Purpur start - Player ridable in water option ++ @Override ++ public boolean dismountsUnderwater() { ++ return !level().purpurConfig.playerRidableInWater; ++ } ++ // Purpur end - Player ridable in water option + + public boolean setEntityOnShoulder(CompoundTag entityCompound) { + if (this.isPassenger() || !this.onGround() || this.isInWater() || this.isInPowderSnow) { diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch new file mode 100644 index 0000000000..1a88cefdc8 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch @@ -0,0 +1,32 @@ +--- a/net/minecraft/world/entity/projectile/AbstractArrow.java ++++ b/net/minecraft/world/entity/projectile/AbstractArrow.java +@@ -74,6 +_,7 @@ + public ItemStack pickupItemStack = this.getDefaultPickupItem(); // Paper - private -> public + @Nullable + public ItemStack firedFromWeapon = null; // Paper - private -> public ++ 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 entityType, Level level) { + super(entityType, level); +@@ -347,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() { // Paper - protected -> public +@@ -559,6 +_,12 @@ + public 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/Snowball.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/Snowball.java.patch new file mode 100644 index 0000000000..7215ffe717 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/Snowball.java.patch @@ -0,0 +1,43 @@ +--- a/net/minecraft/world/entity/projectile/Snowball.java ++++ b/net/minecraft/world/entity/projectile/Snowball.java +@@ -52,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/ThrownEnderpearl.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch new file mode 100644 index 0000000000..f0ac6ccbf6 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch @@ -0,0 +1,23 @@ +--- a/net/minecraft/world/entity/projectile/ThrownEnderpearl.java ++++ b/net/minecraft/world/entity/projectile/ThrownEnderpearl.java +@@ -133,9 +_,10 @@ + return; + } + // CraftBukkit end +- if (this.random.nextFloat() < 0.05F && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { ++ if (this.random.nextFloat() < serverLevel.purpurConfig.enderPearlEndermiteChance && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) { // 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.moveTo(owner.getX(), owner.getY(), owner.getZ(), owner.getYRot(), owner.getXRot()); + serverLevel.addFreshEntity(endermite, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.ENDER_PEARL); + } +@@ -155,7 +_,7 @@ + if (serverPlayer1 != null) { + serverPlayer1.resetFallDistance(); + serverPlayer1.resetCurrentImpulseContext(); +- serverPlayer1.hurtServer(serverPlayer.serverLevel(), this.damageSources().enderPearl().customEventDamager(this), 5.0F); // CraftBukkit // Paper - fix DamageSource API ++ serverPlayer1.hurtServer(serverPlayer.serverLevel(), this.damageSources().enderPearl().customEventDamager(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/projectile/ThrownTrident.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/ThrownTrident.java.patch new file mode 100644 index 0000000000..982cc02057 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/ThrownTrident.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/projectile/ThrownTrident.java ++++ b/net/minecraft/world/entity/projectile/ThrownTrident.java +@@ -64,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/WitherSkull.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/WitherSkull.java.patch new file mode 100644 index 0000000000..deea12ae5d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/WitherSkull.java.patch @@ -0,0 +1,25 @@ +--- a/net/minecraft/world/entity/projectile/WitherSkull.java ++++ b/net/minecraft/world/entity/projectile/WitherSkull.java +@@ -92,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 + this.level().getCraftServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { +@@ -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/raid/Raids.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/raid/Raids.java.patch new file mode 100644 index 0000000000..de4dbac7a6 --- /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 +@@ -25,6 +_,7 @@ + + public class Raids extends SavedData { + private static final String RAID_FILE_ID = "raids"; ++ public final Map playerCooldowns = Maps.newHashMap(); // Purpur - Raid cooldown setting + public final Map raidMap = Maps.newHashMap(); + private final ServerLevel level; + private int nextAvailableID; +@@ -46,6 +_,17 @@ + + public void tick() { + 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()) { +@@ -119,11 +_,13 @@ + */ + + 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 ++ if (level.purpurConfig.raidCooldownSeconds != 0 && playerCooldowns.containsKey(player.getUUID())) return null; // Purpur - Raid cooldown setting + // CraftBukkit start + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callRaidTriggerEvent(raid, player)) { + player.removeEffect(net.minecraft.world.effect.MobEffects.RAID_OMEN); + return null; + } ++ if (level.purpurConfig.raidCooldownSeconds != 0) playerCooldowns.put(player.getUUID(), level.purpurConfig.raidCooldownSeconds); // Purpur - Raid cooldown setting + + if (!raid.isStarted() && !this.raidMap.containsKey(raid.getId())) { + this.raidMap.put(raid.getId(), raid); diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/AbstractBoat.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/AbstractBoat.java.patch new file mode 100644 index 0000000000..430b15eb44 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/AbstractBoat.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/entity/vehicle/AbstractBoat.java ++++ b/net/minecraft/world/entity/vehicle/AbstractBoat.java +@@ -483,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/NewMinecartBehavior.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java.patch new file mode 100644 index 0000000000..dfb044e115 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/vehicle/NewMinecartBehavior.java ++++ b/net/minecraft/world/entity/vehicle/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/OldMinecartBehavior.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java.patch new file mode 100644 index 0000000000..c8ae0ce13c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java.patch @@ -0,0 +1,13 @@ +--- a/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java ++++ b/net/minecraft/world/entity/vehicle/OldMinecartBehavior.java +@@ -279,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 0000000000..bca1c3085b --- /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 +@@ -36,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(); +@@ -84,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 0000000000..87515c815a --- /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 0000000000..bca7321cd8 --- /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 +@@ -65,6 +_,7 @@ + @Nullable + private 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 0000000000..1626289f6a --- /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 (index != 1 && index != 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 0000000000..71cd41ca49 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/inventory/AnvilMenu.java.patch @@ -0,0 +1,200 @@ +--- a/net/minecraft/world/inventory/AnvilMenu.java ++++ b/net/minecraft/world/inventory/AnvilMenu.java +@@ -20,6 +_,12 @@ + import net.minecraft.world.level.block.state.BlockState; + 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; +@@ -49,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); +@@ -74,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.getAbilities().instabuild) { ++ if (this.bypassCost) ((ServerPlayer) player).lastSentExp = -1; else // Purpur - Anvil API + player.giveExperienceLevels(-this.cost.get()); + } + +@@ -126,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)); +@@ -191,23 +_,36 @@ + 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.getAbilities().instabuild || item.is(Items.ENCHANTED_BOOK)) { + canEnchant = true; + } + +- for (Holder holder1 : mutable.keySet()) { ++ // Purpur start - Config to allow unsafe enchants ++ java.util.Iterator> mutableIterator = mutable.keySet().iterator(); ++ while (mutableIterator.hasNext()) { ++ Holder holder1 = mutableIterator.next(); ++ // Purpur end - Config to allow unsafe enchants + if (!holder1.equals(holder) && !Enchantment.areCompatible(holder, holder1)) { +- canEnchant = false; +- i++; ++ canEnchant1 = this.canDoUnsafeEnchants || org.purpurmc.purpur.PurpurConfig.allowIncompatibleEnchants; // Purpur - Anvil API // Purpur - flag3 -> canEnchant1 - Config to allow unsafe enchants ++ // Purpur start - Config to allow unsafe enchants ++ if (!canEnchant1 && org.purpurmc.purpur.PurpurConfig.replaceIncompatibleEnchants) { ++ mutableIterator.remove(); // replace current enchant with the incompatible one trying to be applied // TODO: is this needed? ++ canEnchant1 = true; ++ } ++ // Purpur end - Config to allow unsafe enchants ++ ++i; + } + } + +- 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(); + } + +@@ -236,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)) { +@@ -260,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.getAbilities().instabuild) { // CraftBukkit + itemStack = ItemStack.EMPTY; + } +@@ -280,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 +@@ -288,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 0000000000..c7b2ace186 --- /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 +@@ -42,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 0000000000..8f99026899 --- /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)); +@@ -294,7 +_,7 @@ + @Override + public void removed(Player player) { + super.removed(player); +- this.access.execute((level, blockPos) -> this.clearContainer(player, this.enchantSlots)); ++ this.access.execute((level, blockPos) -> {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 0000000000..1af1db8462 --- /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 +@@ -91,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.award((ServerLevel) level, Vec3.atCenterOf(blockPos), event.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); ++ 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.award((ServerLevel) level, Vec3.atCenterOf(blockPos), grindstoneTakeResultEvent.getExperienceAmount(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); // Purpur - Grindstone API + // Paper end - Fire BlockExpEvent on grindstone use + } + +@@ -124,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); + } + } +@@ -202,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); + } +@@ -222,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; + } + +@@ -278,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 0000000000..9899e30a58 --- /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 0000000000..c50f80b338 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/AxeItem.java.patch @@ -0,0 +1,71 @@ +--- a/net/minecraft/world/item/AxeItem.java ++++ b/net/minecraft/world/item/AxeItem.java +@@ -62,13 +_,15 @@ + if (playerHasShieldUseIntent(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 +@@ -76,8 +_,15 @@ + CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger((ServerPlayer)player, clickedPos, itemInHand); + } + +- level.setBlock(clickedPos, optional.get(), 11); +- level.gameEvent(GameEvent.BLOCK_CHANGE, clickedPos, GameEvent.Context.of(player, optional.get())); ++ // Purpur start - Tool actionable options ++ level.setBlock(clickedPos, state, 11); ++ 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, LivingEntity.getSlotForHand(context.getHand())); + } +@@ -92,22 +_,24 @@ + return context.getHand().equals(InteractionHand.MAIN_HAND) && player.getOffhandItem().is(Items.SHIELD) && !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()) { +- level.playSound(player, pos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0F, 1.0F); ++ level.playSound(WeatheringCopper.getPrevious(state).isPresent() ? player : null, pos, SoundEvents.AXE_SCRAPE, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - Tool actionable options - force sound + level.levelEvent(player, 3005, pos, 0); + 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()) { +- level.playSound(player, pos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0F, 1.0F); ++ level.playSound(HoneycombItem.WAX_OFF_BY_BLOCK.get().containsKey(state.getBlock()) ? player : null, pos, SoundEvents.AXE_WAX_OFF, SoundSource.BLOCKS, 1.0F, 1.0F); // Purpur - Tool actionable options - force sound + level.levelEvent(player, 3004, pos, 0); + return optional; + } else { 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 0000000000..855b75b34b --- /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 +@@ -152,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 + } + + @Nullable +@@ -217,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 customData.loadInto(blockEntity, level.registryAccess()); + } + +@@ -264,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 0000000000..2cad5634b4 --- /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); ++ this.shoot(serverLevel, player, player.getUsedItemHand(), stack, list, powerForTime * 3.0F, (float) serverLevel.purpurConfig.bowProjectileOffset, powerForTime == 1.0F, null); // 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 0000000000..15d7ab0566 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/BucketItem.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/item/BucketItem.java ++++ b/net/minecraft/world/item/BucketItem.java +@@ -147,7 +_,7 @@ + // CraftBukkit end + if (!flag) { + return result != null && this.emptyContents(player, level, result.getBlockPos().relative(result.getDirection()), null, enumdirection, clicked, itemstack, enumhand); // CraftBukkit +- } else if (level.dimensionType().ultraWarm() && this.content.is(FluidTags.WATER)) { ++ } else if ((level.dimensionType().ultraWarm() || (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(); +@@ -156,7 +_,7 @@ + ); + + for (int i = 0; i < 8; i++) { +- level.addParticle(ParticleTypes.LARGE_SMOKE, x + Math.random(), y + Math.random(), z + Math.random(), 0.0, 0.0, 0.0); ++ ((net.minecraft.server.level.ServerLevel) level).sendParticlesSource(null, ParticleTypes.LARGE_SMOKE, false, true, x + Math.random(), y + Math.random(), z + Math.random(), 1, 0.0D, 0.0D, 0.0D, 0.0D); // Purpur - Add allow water in end world option + } + + return true; 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 0000000000..bcb821757e --- /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 +@@ -70,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 0000000000..f18d71793d --- /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 +@@ -123,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 0000000000..fe21990b3e --- /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 +@@ -26,7 +_,7 @@ + if (level instanceof ServerLevel serverLevel) { + // CraftBukkit start + // Paper start - PlayerLaunchProjectileEvent +- final Projectile.Delayed thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, serverLevel, itemInHand, player, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, 1.0F); ++ final Projectile.Delayed thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, serverLevel, itemInHand, player, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, (float) serverLevel.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 0000000000..d46e5ccc90 --- /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 aboveBlockPosition = 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 0000000000..faeb98db17 --- /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 { + // Paper end - PlayerLaunchProjectileEvent + player.containerMenu.sendAllDataToRemote(); 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 0000000000..4e6e9ca20d --- /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 +@@ -46,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 0000000000..d8595672cf --- /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 +@@ -461,6 +_,7 @@ + serverLevel.isBlockPlaceCancelled = true; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent + for (org.bukkit.block.BlockState blockstate : blocks) { + blockstate.update(true, false); ++ ((org.bukkit.craftbukkit.block.CraftBlock) blockstate.getBlock()).getNMS().getBlock().forgetPlacer(); // Purpur - Store placer on Block when placed + } + serverLevel.isBlockPlaceCancelled = false; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent + serverLevel.preventPoiUpdated = false; +@@ -486,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), updateFlag, net.minecraft.world.level.block.Block.UPDATE_LIMIT); // send null chunk as chunk.k() returns false by this point + } +@@ -627,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, Integer.valueOf(0)), 0, this.getMaxDamage()); + } +@@ -1251,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 0000000000..618da92d7c --- /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 +@@ -367,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); +@@ -2010,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 0000000000..c7332498c2 --- /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 +@@ -196,6 +_,7 @@ + public static void renderBiomePreviewMap(ServerLevel serverLevel, ItemStack stack) { + MapItemSavedData savedData = getSavedData(stack, serverLevel); + if (savedData != null) { ++ savedData.isExplorerMap = true; // Purpur - Explorer Map API + if (serverLevel.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 0000000000..2a86393615 --- /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 0000000000..0da20a68d3 --- /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 +@@ -108,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 0000000000..2a462f3a64 --- /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 +@@ -47,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 0000000000..9c09ecb727 --- /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 0000000000..7a0553a75e --- /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 +@@ -57,6 +_,23 @@ + if (level.getBlockEntity(clickedPos) instanceof Spawner spawner) { + if (level.paperConfig().entities.spawning.disableMobSpawnerSpawnEggTransformation) return InteractionResult.FAIL; // Paper - Allow disabling mob spawner spawn egg transformation + EntityType type = this.getType(level.registryAccess(), itemInHand); ++ // 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, 3); + 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 0000000000..e2154c3b84 --- /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 +@@ -23,7 +_,7 @@ + ItemStack itemInHand = player.getItemInHand(hand); + if (level instanceof ServerLevel serverLevel) { + // Paper start - PlayerLaunchProjectileEvent +- final Projectile.Delayed thrownPotion = Projectile.spawnProjectileFromRotationDelayed(ThrownPotion::new, serverLevel, itemInHand, player, -20.0F, PROJECTILE_SHOOT_POWER, 1.0F); ++ final Projectile.Delayed thrownPotion = Projectile.spawnProjectileFromRotationDelayed(ThrownPotion::new, 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 0000000000..3f37caa589 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/TridentItem.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/world/item/TridentItem.java ++++ b/net/minecraft/world/item/TridentItem.java +@@ -90,7 +_,7 @@ + // stack.hurtWithoutBreaking(1, player); // CraftBukkit - moved down + if (tridentSpinAttackStrength == 0.0F) { + Projectile.Delayed tridentDelayed = Projectile.spawnProjectileFromRotationDelayed( // Paper - PlayerLaunchProjectileEvent +- ThrownTrident::new, serverLevel, stack, player, 0.0F, 2.5F, 1.0F ++ ThrownTrident::new, serverLevel, stack, 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()); +@@ -101,6 +_,9 @@ + 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 + thrownTrident.pickupItemStack = stack.copy(); // SPIGOT-4511 update since damage call moved + // CraftBukkit end 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 0000000000..65243f097f --- /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 0000000000..188d2cfb2f --- /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; +@@ -87,6 +_,11 @@ + if (this.isExact()) { + return this.itemStacks.contains(stack); // Paper - Improve exact choice recipe ingredients (hashing FTW!) + } ++ // 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 + // CraftBukkit end + 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 0000000000..918c41cec0 --- /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 +@@ -602,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 0000000000..acab23bbb2 --- /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), true); + // Paper end +- 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 + // Paper start - sort enchantments + private static final Codec>> LEVELS_CODEC = Codec.unboundedMap(Enchantment.CODEC, LEVEL_CODEC) + .xmap(m -> { +@@ -65,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); + } + } +@@ -160,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 0000000000..6a3b114a8a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/item/trading/MerchantOffer.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/world/item/trading/MerchantOffer.java ++++ b/net/minecraft/world/item/trading/MerchantOffer.java +@@ -143,7 +_,12 @@ + } + + public void updateDemand() { +- this.demand = Math.max(0, this.demand + this.uses - (this.maxUses - this.uses)); // Paper - Fix MC-163962 ++ // Purpur start - Configurable minimum demand for trades ++ this.updateDemand(0); ++ } ++ public void updateDemand(int minimumDemand) { ++ this.demand = Math.max(minimumDemand, this.demand + this.uses - (this.maxUses - this.uses)); // Paper - Fix MC-163962 ++ // Purpur end - 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 0000000000..99446bb5af --- /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 +@@ -52,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 0000000000..73e6f74b16 --- /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 +@@ -185,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 0000000000..cfd4c384ed --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch @@ -0,0 +1,84 @@ +--- a/net/minecraft/world/level/Level.java ++++ b/net/minecraft/world/level/Level.java +@@ -170,6 +_,7 @@ + // Paper end - add paper world config + + public final io.papermc.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray ++ public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur - Purpur config files + public static BlockPos lastPhysicsProblem; // Spigot + private org.spigotmc.TickLimiter entityLimiter; + private org.spigotmc.TickLimiter tileLimiter; +@@ -177,6 +_,49 @@ + public final Map explosionDensityCache = new 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; + } +@@ -853,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(), env); // Purpur - Purpur config files ++ this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur - Add adjustable breeding cooldown to config + this.generator = gen; + this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); + +@@ -2130,4 +_,14 @@ + return this.id; + } + } ++ ++ // 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 0000000000..958b8eb2af --- /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 +@@ -267,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/patches/server/0273-Add-PreExplodeEvents.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/ServerExplosion.java.patch similarity index 63% rename from patches/server/0273-Add-PreExplodeEvents.patch rename to purpur-server/minecraft-patches/sources/net/minecraft/world/level/ServerExplosion.java.patch index 5b9aefa415..102f56eadc 100644 --- a/patches/server/0273-Add-PreExplodeEvents.patch +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/ServerExplosion.java.patch @@ -1,14 +1,20 @@ -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/ServerExplosion.java b/src/main/java/net/minecraft/world/level/ServerExplosion.java -index 05fdb02b6f73c24f6985755effecf92c0b365cf0..4c7e4683c53afb0800b7f17c5964ba8ff31848d1 100644 ---- a/src/main/java/net/minecraft/world/level/ServerExplosion.java -+++ b/src/main/java/net/minecraft/world/level/ServerExplosion.java -@@ -670,6 +670,23 @@ public class ServerExplosion implements Explosion { +--- a/net/minecraft/world/level/ServerExplosion.java ++++ b/net/minecraft/world/level/ServerExplosion.java +@@ -319,7 +_,7 @@ + ) { + this.level = level; + this.source = source; +- this.radius = (float) Math.max(radius, 0.0); // CraftBukkit - clamp bad values ++ this.radius = (float) (level == null || level.purpurConfig.explosionClampRadius ? Math.max(radius, 0.0) : radius); // CraftBukkit - clamp bad values // Purpur - Config to remove explosion radius clamp + this.center = center; + this.fire = fire; + this.blockInteraction = blockInteraction; +@@ -649,10 +_,27 @@ + + public void explode() { + // CraftBukkit start +- if (this.radius < 0.1F) { ++ if ((this.level == null || this.level.purpurConfig.explosionClampRadius) && this.radius < 0.1F) { // Purpur - Config to remove explosion radius clamp return; } // CraftBukkit end @@ -28,7 +34,7 @@ index 05fdb02b6f73c24f6985755effecf92c0b365cf0..4c7e4683c53afb0800b7f17c5964ba8f + return; + } + } -+ // Purpur end ++ // Purpur end - Add PreExplodeEvents // Paper start - collision optimisations this.blockCache = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(); this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH]; diff --git a/patches/server/0234-Anvil-repair-damage-options.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/AnvilBlock.java.patch similarity index 62% rename from patches/server/0234-Anvil-repair-damage-options.patch rename to purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/AnvilBlock.java.patch index 52564796d8..7522ef9071 100644 --- a/patches/server/0234-Anvil-repair-damage-options.patch +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/AnvilBlock.java.patch @@ -1,18 +1,10 @@ -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 50c907c962f936d2035bb7550750cdbd220b29c2..f9a2d2d4f798efa0d691996ec5ff7fe00260b36c 100644 ---- a/src/main/java/net/minecraft/world/level/block/AnvilBlock.java -+++ b/src/main/java/net/minecraft/world/level/block/AnvilBlock.java -@@ -59,6 +59,53 @@ public class AnvilBlock extends FallingBlock { - return this.defaultBlockState().setValue(FACING, ctx.getHorizontalDirection().getClockWise()); +--- a/net/minecraft/world/level/block/AnvilBlock.java ++++ b/net/minecraft/world/level/block/AnvilBlock.java +@@ -59,6 +_,53 @@ + return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getClockWise()); } -+ // Purpur start - repairable/damageable anvils ++ // 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)) { @@ -57,26 +49,8 @@ index 50c907c962f936d2035bb7550750cdbd220b29c2..f9a2d2d4f798efa0d691996ec5ff7fe0 + } + return net.minecraft.world.InteractionResult.TRY_WITH_EMPTY_HAND; + } -+ // Purpur end ++ // Purpur end - Anvil repair/damage options + @Override - protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) { - if (!world.isClientSide) { -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 13791cc140fa7643991f5c958f48bf4693b20895..1290e73e4e741e7530481885994f16c2fd5db32c 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -797,9 +797,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; + 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 0000000000..066e2cfd65 --- /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 0000000000..fd6497469a --- /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 0000000000..69cb911584 --- /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 (!BedBlock.canSetSpawn(level)) return this.explodeBed(state, level, pos); // Paper - check explode first +@@ -148,7 +_,7 @@ + } + + Vec3 center = pos.getCenter(); +- level.explode(null, level.damageSources().badRespawnPointExplosion(center, blockState), null, center, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state ++ if (level.purpurConfig.bedExplode) level.explode(null, level.damageSources().badRespawnPointExplosion(center, 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 +@@ -169,7 +_,7 @@ + + @Override + public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) { +- super.fallOn(level, state, pos, entity, fallDistance * 0.5F); ++ 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 0000000000..45532dcb86 --- /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 +@@ -261,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 0000000000..7b348e9e65 --- /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 +@@ -90,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 || +@@ -299,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 +@@ -317,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(itemStack -> popResource((ServerLevel)level, pos, itemStack)); ++ getDrops(state, (ServerLevel)level, pos, blockEntity).forEach(itemStack -> popResource((ServerLevel)level, pos, applyLoreFromTile(itemStack, blockEntity))); // Purpur - Persistent BlockEntity Lore and DisplayName + state.spawnAfterBreak((ServerLevel)level, pos, ItemStack.EMPTY, true); + } + } +@@ -329,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(itemStack -> popResource(level, pos, itemStack)); ++ getDrops(state, (ServerLevel)level, pos, blockEntity, entity, tool).forEach(itemStack -> popResource(level, pos, applyLoreFromTile(itemStack, 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); +@@ -412,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(); +@@ -423,7 +_,7 @@ + } + + public void fallOn(Level level, 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 - 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 0000000000..be0a86f425 --- /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 +@@ -6486,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() +@@ -6497,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 0000000000..5b62f1261d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BubbleColumnBlock.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/world/level/block/BubbleColumnBlock.java ++++ b/net/minecraft/world/level/block/BubbleColumnBlock.java +@@ -124,10 +_,10 @@ + if (blockState.is(Blocks.BUBBLE_COLUMN)) { + return blockState; + } else if (blockState.is(Blocks.SOUL_SAND)) { +- return Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(false)); ++ return Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(org.purpurmc.purpur.PurpurConfig.soulSandBlockReverseBubbleColumnFlow)); // Purpur - Config to reverse bubble column flow + } else { + return blockState.is(Blocks.MAGMA_BLOCK) +- ? Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(true)) ++ ? Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(!org.purpurmc.purpur.PurpurConfig.magmaBlockReverseBubbleColumnFlow)) // Purpur - Config to reverse bubble column flow + : Blocks.WATER.defaultBlockState(); + } + } diff --git a/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BushBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BushBlock.java.patch new file mode 100644 index 0000000000..b166eb72a8 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/BushBlock.java.patch @@ -0,0 +1,27 @@ +--- a/net/minecraft/world/level/block/BushBlock.java ++++ b/net/minecraft/world/level/block/BushBlock.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/CactusBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CactusBlock.java.patch new file mode 100644 index 0000000000..64fecea919 --- /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 +@@ -21,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; +@@ -104,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.purpurConfig.cactusBreaksFromSolidNeighbors && blockState.isSolid()) || level.getFluidState(pos.relative(direction)).is(FluidTags.LAVA)) { // Purpur - Cactus breaks from solid neighbors config + return false; + } + } +@@ -128,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 0000000000..f0b3fec971 --- /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 +@@ -119,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 0000000000..d15f9f5a9f --- /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 +@@ -141,7 +_,7 @@ + return this.defaultBlockState() + .setValue(WATERLOGGED, Boolean.valueOf(flag)) + .setValue(SIGNAL_FIRE, Boolean.valueOf(this.isSmokeSource(level.getBlockState(clickedPos.below())))) +- .setValue(LIT, Boolean.valueOf(!flag)) ++ .setValue(LIT, Boolean.valueOf(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 0000000000..ba95ddd13b --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/CarvedPumpkinBlock.java.patch @@ -0,0 +1,36 @@ +--- a/net/minecraft/world/level/block/CarvedPumpkinBlock.java ++++ b/net/minecraft/world/level/block/CarvedPumpkinBlock.java +@@ -64,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 + } + } else { + BlockPattern.BlockPatternMatch blockPatternMatch1 = this.getOrCreateIronGolemFull().find(level, pos); +@@ -72,13 +_,23 @@ + 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 + } + } + } + } + + 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) { ++ if (golem instanceof SnowGolem snowGolem) { ++ snowGolem.setSummoner(placer == null ? null : placer.getUUID()); ++ } else if (golem instanceof IronGolem ironGolem) { ++ ironGolem.setSummoner(placer == null ? null : placer.getUUID()); ++ } ++ // Purpur end - Summoner API + // clearPatternBlocks(level, patternMatch); // CraftBukkit - moved down + golem.moveTo(pos.getX() + 0.5, pos.getY() + 0.05, pos.getZ() + 0.5, 0.0F, 0.0F); + if (!level.addFreshEntity(golem, (golem.getType() == EntityType.SNOW_GOLEM) ? org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BUILD_SNOWMAN : org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BUILD_IRONGOLEM)) { 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 0000000000..6ead2196bb --- /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 0000000000..829a79ee64 --- /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, Boolean.valueOf(true)), 2); + } ++ ++ // 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 0000000000..54d2fa4637 --- /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 0000000000..c2e63ab56a --- /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 +@@ -357,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 0000000000..2be94eb481 --- /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 +@@ -241,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(1500, 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 iblockdata1 = ComposterBlock.addItem(player, state, level, pos, stack); ++ // Paper start - handle cancelled events ++ if (iblockdata1 == null) { ++ return null; ++ } ++ // Paper end ++ ++ level.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0); ++ player.awardStat(Stats.ITEM_USED.get(stack.getItem())); ++ stack.consume(1, player); ++ return iblockdata1; ++ } ++ 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 0000000000..690cfe031f --- /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 0000000000..f1bbca32a1 --- /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 +@@ -182,7 +_,7 @@ + @Override + protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { + 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().getBoolean(GameRules.RULE_MOBGRIEFING))) { // 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().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Purpur - Configurable ravager griefable blocks list + serverLevel.destroyBlock(pos, true, entity); + } + +@@ -217,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 0000000000..e2733acaa4 --- /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 +@@ -206,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, 10); +@@ -294,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, 3); ++ level.sendBlockUpdated(otherPos, otherState, otherState, 3); ++ 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 0000000000..d08f61da62 --- /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/EnchantingTableBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/EnchantingTableBlock.java.patch new file mode 100644 index 0000000000..6610cced49 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/EnchantingTableBlock.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/world/level/block/EnchantingTableBlock.java ++++ b/net/minecraft/world/level/block/EnchantingTableBlock.java +@@ -119,4 +_,18 @@ + protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) { + return false; + } ++ ++ // Purpur start - Enchantment Table Persists Lapis ++ @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 EnchantingTableBlockEntity enchantmentTable) { ++ net.minecraft.world.Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.LAPIS_LAZULI, enchantmentTable.getLapis())); ++ level.updateNeighbourForOutputSignal(pos, this); ++ } ++ ++ super.onRemove(state, level, pos, newState, moved); ++ } ++ // Purpur end - Enchantment Table Persists Lapis + } 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 0000000000..7f0aa19b6a --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/FarmBlock.java.patch @@ -0,0 +1,48 @@ +--- 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, float 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.5F ++ && (serverLevel.purpurConfig.farmlandTrampleHeight >= 0D ? fallDistance >= serverLevel.purpurConfig.farmlandTrampleHeight : level.random.nextFloat() < fallDistance - 0.5F) // // Purpur - Configurable farmland trample height + && entity instanceof LivingEntity + && (entity instanceof Player || serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) + && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) { +@@ -129,6 +_,27 @@ + 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) { ++ java.util.Iterator armor = ((LivingEntity) entity).getArmorSlots().iterator(); ++ if (armor.hasNext() && net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FEATHER_FALLING, armor.next()) >= (int) entity.fallDistance) { ++ return; ++ } ++ } ++ // Purpur end - Farmland trampling changes ++ + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.DIRT.defaultBlockState())) { + return; + } +@@ -174,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 0000000000..f9b8e4aac2 --- /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, Integer.valueOf(random.nextInt(25))); ++ return this.defaultBlockState().setValue(AGE, Integer.valueOf(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, Integer.valueOf(25)); ++ return state.setValue(AGE, Integer.valueOf(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) { +@@ -130,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, Integer.valueOf(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 + } + } + +@@ -148,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 0000000000..d8013d2da4 --- /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, float 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 0000000000..0f5251d574 --- /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 +@@ -40,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.dimensionType().ultraWarm()) { ++ if (level.isNether() || (level.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) { // Purpur - Add allow water in end world option + level.removeBlock(pos, false); + return; + } +@@ -65,7 +_,7 @@ + return; + } + // CraftBukkit end +- if (level.dimensionType().ultraWarm()) { ++ if (level.isNether() || (level.isTheEnd() && !org.purpurmc.purpur.PurpurConfig.allowWaterPlacementInTheEnd)) { // Purpur - Add allow water in end world option + level.removeBlock(pos, false); + } else { + level.setBlockAndUpdate(pos, meltsInto()); 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 0000000000..b0ce9ac1aa --- /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 +@@ -72,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 0000000000..ef05599422 --- /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 +@@ -134,7 +_,7 @@ + + @Override + protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) { +- 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 + } + } +@@ -169,7 +_,7 @@ + BlockState neighborState, + RandomSource random + ) { +- if (state.getFluidState().isSource() || neighborState.getFluidState().isSource()) { ++ if (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)); + } + +@@ -178,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 0000000000..52ccc72776 --- /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().directBlock(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 0000000000..282b160de0 --- /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 +@@ -72,7 +_,7 @@ + protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + if (level.spigotConfig.enableZombiePigmenPortalSpawns && level.dimensionType().natural() // Spigot + && level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) +- && random.nextInt(2000) < level.getDifficulty().getId()) { ++ && random.nextInt(level.purpurConfig.piglinPortalSpawnModifier) < level.getDifficulty().getId()) { // Purpur - Piglin portal spawn modifier + while (level.getBlockState(pos).is(this)) { + pos = pos.below(); + } +@@ -129,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() + .getInt( 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 0000000000..95b1bb2c18 --- /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 BushBlock { ++public class NetherWartBlock extends BushBlock 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; +@@ -70,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 0000000000..b9260673f7 --- /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 +@@ -107,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 0000000000..3202a8cee4 --- /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.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 0000000000..853ec1c3a9 --- /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 +@@ -197,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 0000000000..2b31f8cfba --- /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 +@@ -34,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 0000000000..d8a78eb77b --- /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 +@@ -159,7 +_,7 @@ + }; + Vec3 center = pos2.getCenter(); + level.explode( +- null, level.damageSources().badRespawnPointExplosion(center, blockState), explosionDamageCalculator, center, 5.0F, true, Level.ExplosionInteraction.BLOCK // CraftBukkit - add state ++ null, level.damageSources().badRespawnPointExplosion(center, 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 0000000000..db79f0f166 --- /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 +@@ -134,7 +_,7 @@ + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState() +- .setValue(WATERLOGGED, Boolean.valueOf(context.getLevel().getFluidState(context.getClickedPos()).getType() == Fluids.WATER)); ++ .setValue(WATERLOGGED, Boolean.valueOf(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 0000000000..ec93007cc9 --- /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 +@@ -150,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 0000000000..f6406d510e --- /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 +@@ -96,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.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 0000000000..cf68daf1bf --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/SpawnerBlock.java.patch @@ -0,0 +1,68 @@ +--- a/net/minecraft/world/level/block/SpawnerBlock.java ++++ b/net/minecraft/world/level/block/SpawnerBlock.java +@@ -43,6 +_,57 @@ + ); + } + ++ // 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) { ++ type = net.minecraft.world.entity.EntityType.by(nextSpawnData.getEntityToSpawn()); ++ net.minecraft.world.level.SpawnData.CODEC.encodeStart(net.minecraft.nbt.NbtOps.INSTANCE, nextSpawnData).result().ifPresent(tag -> item.set(net.minecraft.core.component.DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY.update(compoundTag -> compoundTag.put("Purpur.SpawnData", tag)))); ++ } ++ ++ 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)); ++ } ++ ++ List lore = level.purpurConfig.silkTouchSpawnerLore; ++ if (lore != null && !lore.isEmpty()) { ++ ++ 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.HIDE_ADDITIONAL_TOOLTIP, net.minecraft.util.Unit.INSTANCE); ++ } ++ 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); +@@ -51,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 0000000000..ab343ffb35 --- /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 0000000000..df3ab7800f --- /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 +@@ -93,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().directBlock(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 0000000000..b39dc02a33 --- /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; + protected static final float AABB_OFFSET = 6.0F; +@@ -113,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 0000000000..d6e5654f9b --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/TurtleEggBlock.java.patch @@ -0,0 +1,47 @@ +--- a/net/minecraft/world/level/block/TurtleEggBlock.java ++++ b/net/minecraft/world/level/block/TurtleEggBlock.java +@@ -157,7 +_,7 @@ + + private boolean shouldUpdateHatchLevel(Level level) { + float timeOfDay = level.getTimeOfDay(1.0F); +- return timeOfDay < 0.69 && timeOfDay > 0.65 || level.random.nextInt(500) == 0; ++ return timeOfDay < 0.69 && timeOfDay > 0.65 || level.random.nextInt(level.purpurConfig.turtleEggsRandomTickCrackChance) == 0; // Purpur - Turtle eggs random tick crack chance + } + + @Override +@@ -192,9 +_,31 @@ + } + + private boolean canDestroyEgg(ServerLevel level, Entity entity) { +- return !(entity instanceof Turtle) +- && !(entity instanceof Bat) +- && entity instanceof LivingEntity +- && (entity instanceof Player || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)); ++ // 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.AbstractMinecart) { ++ return true; ++ } ++ if (!(entity instanceof LivingEntity)) { ++ return false; ++ } ++ // Purpur start - Option to disable turtle egg trampling with feather falling ++ if (level.purpurConfig.turtleEggsTramplingFeatherFalling) { ++ java.util.Iterator armor = ((LivingEntity) entity).getArmorSlots().iterator(); ++ return !armor.hasNext() || net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(net.minecraft.world.item.enchantment.Enchantments.FEATHER_FALLING, armor.next()) < (int) entity.fallDistance; ++ } ++ // Purpur end - Option to disable turtle egg trampling with feather falling ++ if (entity instanceof Player) return true; ++ ++ return level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); ++ // 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 0000000000..89633d8f77 --- /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/WeepingVinesBlock.java.patch b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/WeepingVinesBlock.java.patch new file mode 100644 index 0000000000..be28707f28 --- /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 0000000000..d92c3e43bd --- /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 +@@ -71,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 0000000000..71426f3868 --- /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 +@@ -191,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(); +@@ -274,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 0000000000..8987119ae0 --- /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 +@@ -139,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; +@@ -168,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(); +@@ -197,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 (beaconBeamSection == null || blockState.getLightBlock() >= 15 && !blockState.is(Blocks.BEDROCK)) { + blockEntity.checkingBeamSections.clear(); + blockEntity.lastCheckY = height; +@@ -216,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 0000000000..091db9f55a --- /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 +@@ -76,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; +@@ -154,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(); +@@ -408,8 +_,8 @@ + return this.stored.stream().map(BeehiveBlockEntity.BeeData::toOccupant).toList(); + } + +- 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 0000000000..49b53bf98d --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch @@ -0,0 +1,50 @@ +--- a/net/minecraft/world/level/block/entity/BlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BlockEntity.java +@@ -84,6 +_,14 @@ + this.persistentDataContainer.putAll((CompoundTag) persistentDataTag); + } + // Paper end - read persistent data container ++ ++ // Purpur start - Persistent BlockEntity Lore and DisplayName ++ if (tag.contains("Purpur.persistentLore")) { ++ net.minecraft.world.item.component.ItemLore.CODEC.decode(net.minecraft.nbt.NbtOps.INSTANCE, tag.getCompound("Purpur.persistentLore")).result() ++ .ifPresent(tag1 -> this.persistentLore = tag1.getFirst()); ++ } ++ // Purpur end - Persistent BlockEntity Lore and DisplayName ++ + } + + public final void loadWithComponents(CompoundTag tag, HolderLookup.Provider registries) { +@@ -98,6 +_,15 @@ + this.loadAdditional(tag, registries); + } + ++ // Purpur start - Persistent BlockEntity Lore and DisplayName ++ protected void saveAdditional(CompoundTag nbt) { ++ if (this.persistentLore != null) { ++ net.minecraft.world.item.component.ItemLore.CODEC.encodeStart(net.minecraft.nbt.NbtOps.INSTANCE, this.persistentLore).result() ++ .ifPresent(tag -> nbt.put("Purpur.persistentLore", tag)); ++ } ++ } ++ // Purpur end - Persistent BlockEntity Lore and DisplayName ++ + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + } + +@@ -378,4 +_,16 @@ + + T getOrDefault(DataComponentType component, T defaultValue); + } ++ // Purpur start - Persistent BlockEntity Lore and DisplayName ++ @Nullable ++ private net.minecraft.world.item.component.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 0000000000..f9491387f9 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch @@ -0,0 +1,74 @@ +--- a/net/minecraft/world/level/block/entity/ConduitBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/ConduitBlockEntity.java +@@ -155,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); + } +@@ -170,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; + } +@@ -213,17 +_,17 @@ + blockEntity.destroyTargetUUID = null; + } else if (blockEntity.destroyTarget == null) { + List entitiesOfClass = level.getEntitiesOfClass( +- LivingEntity.class, getDestroyRangeAABB(pos), collidedEntity -> collidedEntity instanceof Enemy && collidedEntity.isInWaterOrRain() ++ LivingEntity.class, getDestroyRangeAABB(pos, level), collidedEntity -> collidedEntity instanceof Enemy && collidedEntity.isInWaterOrRain() // Purpur - Conduit behavior configuration + ); + if (!entitiesOfClass.isEmpty()) { + blockEntity.destroyTarget = entitiesOfClass.get(level.random.nextInt(entitiesOfClass.size())); + } +- } else if (!blockEntity.destroyTarget.isAlive() || !pos.closerThan(blockEntity.destroyTarget.blockPosition(), 8.0)) { ++ } else if (!blockEntity.destroyTarget.isAlive() || !pos.closerThan(blockEntity.destroyTarget.blockPosition(), level.purpurConfig.conduitDamageDistance)) { // Purpur - Conduit behavior configuration + blockEntity.destroyTarget = null; + } + + if (damageTarget && blockEntity.destroyTarget != null) { // CraftBukkit +- if (blockEntity.destroyTarget.hurtServer((net.minecraft.server.level.ServerLevel) level, level.damageSources().magic().directBlock(level, pos), 4.0F)) // CraftBukkit ++ if (blockEntity.destroyTarget.hurtServer((net.minecraft.server.level.ServerLevel) level, level.damageSources().magic().directBlock(level, pos), level.purpurConfig.conduitDamageAmount)) // CraftBukkit // Purpur - Conduit behavior configuration + level.playSound( + null, + blockEntity.destroyTarget.getX(), +@@ -253,16 +_,22 @@ + } + + public static AABB getDestroyRangeAABB(BlockPos pos) { ++ // Purpur start - Conduit behavior configuration ++ return getDestroyRangeAABB(pos, null); ++ } ++ ++ private static AABB getDestroyRangeAABB(BlockPos pos, Level level) { ++ // Purpur end - Conduit behavior configuration + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); +- return new AABB(x, y, z, x + 1, y + 1, z + 1).inflate(8.0); ++ return new AABB(x, y, z, x + 1, y + 1, z + 1).inflate(level == null ? 8.0 : level.purpurConfig.conduitDamageDistance); // Purpur - Conduit behavior configuration + } + + @Nullable + private static LivingEntity findDestroyTarget(Level level, BlockPos pos, UUID targetId) { + List entitiesOfClass = level.getEntitiesOfClass( +- LivingEntity.class, getDestroyRangeAABB(pos), collidedEntity -> collidedEntity.getUUID().equals(targetId) ++ LivingEntity.class, getDestroyRangeAABB(pos, level), collidedEntity -> collidedEntity.getUUID().equals(targetId) // Purpur - Conduit behavior configuration + ); + return entitiesOfClass.size() == 1 ? entitiesOfClass.get(0) : null; + } 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 0000000000..5f6a1f407c --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java.patch @@ -0,0 +1,41 @@ +--- a/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/EnchantingTableBlockEntity.java +@@ -28,6 +_,7 @@ + private static final RandomSource RANDOM = RandomSource.create(); + @Nullable + private Component name; ++ private int lapis = 0; // Purpur - Enchantment Table Persists Lapis + + public EnchantingTableBlockEntity(BlockPos pos, BlockState state) { + super(BlockEntityType.ENCHANTING_TABLE, pos, state); +@@ -39,6 +_,7 @@ + if (this.hasCustomName()) { + tag.putString("CustomName", Component.Serializer.toJson(this.name, registries)); + } ++ tag.putInt("Purpur.Lapis", this.lapis); // Purpur - Enchantment Table Persists Lapis + } + + @Override +@@ -47,6 +_,7 @@ + if (tag.contains("CustomName", 8)) { + this.name = parseCustomNameSafe(tag.getString("CustomName"), registries); + } ++ this.lapis = tag.getInt("Purpur.Lapis"); // Purpur - Enchantment Table Persists Lapis + } + + public static void bookAnimationTick(Level level, BlockPos pos, BlockState state, EnchantingTableBlockEntity enchantingTable) { +@@ -138,4 +_,14 @@ + public void removeComponentsFromTag(CompoundTag tag) { + tag.remove("CustomName"); + } ++ ++ // Purpur start - Enchantment Table Persists Lapis ++ public int getLapis() { ++ return this.lapis; ++ } ++ ++ public void setLapis(int lapis) { ++ this.lapis = lapis; ++ } ++ // 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 0000000000..96784944d4 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch @@ -0,0 +1,66 @@ +--- a/net/minecraft/world/level/block/entity/SignBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/SignBlockEntity.java +@@ -163,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 + ); + } + } +@@ -310,6 +_,28 @@ + // CraftBukkit - this + return new CommandSourceStack(commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel)level, 2, 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) { ++ final CompoundTag nbt = new CompoundTag(); ++ this.saveAdditional(nbt, this.getLevel().registryAccess()); ++ 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, (blockEntity, registryAccess) -> nbt); ++ } ++ // 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 0000000000..bc62936061 --- /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 0000000000..262a755d67 --- /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 +@@ -106,6 +_,7 @@ + } + // Paper end - Entity load/save limit per chunk + CompoundTag compoundTag1 = new CompoundTag(); ++ if (!entity.canSaveToDisk()) return; // Purpur - Add canSaveToDisk to Entity + if (entity.save(compoundTag1)) { + 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 0000000000..aa18885972 --- /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 +@@ -283,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 0000000000..8c587ee006 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/levelgen/PhantomSpawner.java.patch @@ -0,0 +1,32 @@ +--- a/net/minecraft/world/level/levelgen/PhantomSpawner.java ++++ b/net/minecraft/world/level/levelgen/PhantomSpawner.java +@@ -43,7 +_,7 @@ + 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 + return 0; + } else { + int i = 0; +@@ -51,9 +_,9 @@ + 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 i1 = Mth.clamp(stats.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE); + int i2 = 24000; +@@ -65,7 +_,7 @@ + FluidState fluidState = level.getFluidState(blockPos1); + if (NaturalSpawner.isValidEmptySpawnBlock(level, blockPos1, blockState, fluidState, EntityType.PHANTOM)) { + SpawnGroupData spawnGroupData = null; +- int i3 = 1 + randomSource.nextInt(currentDifficultyAt.getDifficulty().getId() + 1); ++ int i3 = level.purpurConfig.phantomSpawnMinPerAttempt + level.random.nextInt((level.purpurConfig.phantomSpawnMaxPerAttempt < 0 ? currentDifficultyAt.getDifficulty().getId() : level.purpurConfig.phantomSpawnMaxPerAttempt - level.purpurConfig.phantomSpawnMinPerAttempt) + 1); // Purpur - Add phantom spawning options + + for (int i4 = 0; i4 < i3; i4++) { + // 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 0000000000..78e131bae9 --- /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 +@@ -232,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)) { +@@ -319,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 blockState, Direction direction, FluidState fluidState) { + if (blockState.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 0000000000..2d7d530ae5 --- /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 +@@ -177,7 +_,7 @@ + + @Override + public int getTickDelay(LevelReader level) { +- return level.dimensionType().ultraWarm() ? 10 : 30; ++ return level.dimensionType().ultraWarm() ? level.getWorldBorder().world.purpurConfig.lavaSpeedNether : level.getWorldBorder().world.purpurConfig.lavaSpeedNotNether; // Purpur - Make lava flow speed configurable + } + + @Override +@@ -198,6 +_,13 @@ + private void fizz(LevelAccessor level, BlockPos pos) { + level.levelEvent(1501, 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 0000000000..88d21ec4e3 --- /dev/null +++ b/purpur-server/minecraft-patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/world/level/material/WaterFluid.java ++++ b/net/minecraft/world/level/material/WaterFluid.java +@@ -74,6 +_,12 @@ + protected boolean canConvertToSource(ServerLevel level) { + return level.getGameRules().getBoolean(GameRules.RULE_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 0000000000..ef04724ea5 --- /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 +@@ -240,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); +@@ -493,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 0000000000..925647acce --- /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 +@@ -28,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 0000000000..1260ca9e55 --- /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 +@@ -68,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 0000000000..bc7b091815 --- /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.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 0000000000..db94aa7249 --- /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 +@@ -442,4 +_,10 @@ + center.x - xSize / 2.0, center.y - ySize / 2.0, center.z - zSize / 2.0, center.x + xSize / 2.0, center.y + ySize / 2.0, center.z + zSize / 2.0 + ); + } ++ ++ // 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/0001-Rebrand.patch b/purpur-server/paper-patches/features/0001-Rebrand.patch similarity index 59% rename from patches/server/0001-Rebrand.patch rename to purpur-server/paper-patches/features/0001-Rebrand.patch index 47f82e3e50..6a8a0f7d5d 100644 --- a/patches/server/0001-Rebrand.patch +++ b/purpur-server/paper-patches/features/0001-Rebrand.patch @@ -4,349 +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 2da91ed6363c0851e4c459188f5e8ef5475e0c97..8e1d6848e5da82db4c38c27af6bef71c6d843036 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(":purpur-api")) // Pufferfish // Paper // Purpur - Rebrand - 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,10 @@ 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") - -+ implementation("org.mozilla:rhino-runtime:1.7.14") // Purpur - Rebrand -+ implementation("org.mozilla:rhino-engine:1.7.14") // Purpur - Rebrand -+ implementation("dev.omega24:upnp4j:1.0") // Purpur - Rebrand -+ - 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") -@@ -100,14 +104,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 "Purpur", // Pufferfish // Purpur - Rebrand - "Implementation-Version" to implementationVersion, - "Implementation-Vendor" to date, // Paper -- "Specification-Title" to "Paper", -+ "Specification-Title" to "Purpur", // Pufferfish // Purpur - Rebrand - "Specification-Version" to project.version, -- "Specification-Vendor" to "Paper Team", -- "Brand-Id" to "papermc:paper", -- "Brand-Name" to "Paper", -+ "Specification-Vendor" to "Purpur Team", // Pufferfish // Purpur - Rebrand -+ "Brand-Id" to "purpurmc:purpur", // Pufferfish // Purpur - Rebrand -+ "Brand-Name" to "Purpur", // Pufferfish // Purpur - Rebrand - "Build-Number" to (build ?: ""), - "Build-Time" to Instant.now().toString(), - "Git-Branch" to gitBranch, // Paper -@@ -173,7 +177,7 @@ fun TaskContainer.registerRunTask( - name: String, - block: JavaExec.() -> Unit - ): TaskProvider = register(name) { -- group = "paper" -+ group = "paperweight" // Purpur - Rebrand - 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 532306cacd52579cdf37e4aca25887b1ed3ba6a1..fe66e43c27e0798770e102d1385bacbaa90bda07 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -+++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -@@ -35,7 +35,10 @@ public class PaperVersionFetcher implements VersionFetcher { - 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 +52,7 @@ public class PaperVersionFetcher implements VersionFetcher { - 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 +60,7 @@ public class PaperVersionFetcher implements VersionFetcher { - } - - 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 +74,10 @@ public class PaperVersionFetcher implements VersionFetcher { - } - - 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 +89,15 @@ public class PaperVersionFetcher implements VersionFetcher { - 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 - Charsets.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 +141,6 @@ public class PaperVersionFetcher implements VersionFetcher { - return null; - } - -- return text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC); -+ return text("Previous: " + oldVersion, NamedTextColor.GRAY); // Purpur - Rebrand - } - } -diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java -index 6ee39b534b8d992655bc0cef3c299d12cbae0034..bc7e4e5560708fea89c584b1d8b471f4966f311a 100644 ---- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java -+++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java -@@ -20,7 +20,7 @@ public final class PaperConsole extends SimpleTerminalConsole { - @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/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java -index 790bad0494454ca12ee152e3de6da3da634d9b20..b36e30fd4057a938e4d90cb42a2dca661f16478e 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_PURPUR_NAME = "Purpur"; // Purpur - Rebrand - - 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_PURPUR_ID), // Purpur - Fix pufferfish issues // Purpur - Rebrand - getManifestAttribute(manifest, ATTRIBUTE_BRAND_NAME) -- .orElse(BRAND_PAPER_NAME), -+ .orElse(BRAND_PURPUR_NAME), // Purpur - Fix pufferfish issues // Purpur - Rebrand - 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 // Purpur - Rebrand - } - - @Override -diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java -index b24265573fdef5d9a964bcd76146f34542c420cf..710477ae27ebc5afdf0012ef0867d05efd293c24 100644 ---- a/src/main/java/net/minecraft/CrashReport.java -+++ b/src/main/java/net/minecraft/CrashReport.java -@@ -32,6 +32,7 @@ public class CrashReport { - 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 message, Throwable cause) { - io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(cause); // Paper -@@ -144,7 +145,7 @@ public class CrashReport { - } - - public String getFriendlyReport(ReportType type) { -- return this.getFriendlyReport(type, List.of()); -+ return this.getFriendlyReport(type, extraInfo); // Purpur - Rebrand - } - - @Nullable -@@ -191,7 +192,7 @@ public class CrashReport { - } - - 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/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -index e40665cead218502b44dd49051a53326ed94f061..a68f27288604b6f6755efe3c8ea612e295cb1656 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -@@ -289,7 +289,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - - // 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/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java -index 4e56018b64d11f76c8da43fd8f85c6de72204e36..36cec3ed39807e85013e4e3b98c979d7af37ce58 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 - Rebrand -+ String[] parts = message.split("\n"); -+ for (String part : parts) { -+ this.sendRawMessage(part); -+ } -+ // Purpur end - Rebrand - } - - @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 - Rebrand - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index 1354ccfbf525e5e64483ac5f443cc2325ba63850..2e7c3d4befeb6256ce81ecaa9ed4e8fbcb21651e 100644 ---- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -@@ -491,7 +491,7 @@ public class CraftScheduler implements BukkitScheduler { - 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/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -index 15892c7769caa15f3d52a1ee2147cf9615aa0e25..d60e190ce5bfb0f57d282b8471faf61de5977076 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 com.destroystokyo.paper.PaperVersionFetcher(); // Pufferfish // Purpur - Rebrand - } - - @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..99eb04643fce44c37fd96c99756837ccafe7b559 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/org.purpurmc.purpur/purpur-api/pom.properties"); // Pufferfish // Purpur - Rebrand - 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 f7a4fee9bb25ff256dc2e5ea26bfbceca6a49167..47f168b2d62c9a0eebdd8ab678afd857e7622571 100644 ---- a/src/main/java/org/spigotmc/WatchdogThread.java -+++ b/src/main/java/org/spigotmc/WatchdogThread.java -@@ -96,7 +96,7 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre - - 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; - earlyWarningEvery = Math.min(io.papermc.paper.configuration.GlobalConfiguration.get().watchdog.earlyWarningEvery, timeoutTime); // Paper -@@ -155,14 +155,14 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre - 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 - Rebrand - 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 - Rebrand - 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 - Rebrand - // - if ( net.minecraft.world.level.Level.lastPhysicsProblem != null ) - { -@@ -184,12 +184,12 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre - // 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 - Rebrand - 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 - Rebrand - ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(MinecraftServer.getServer(), isLongTimeout); // Paper - rewrite chunk system - this.dumpTickingInfo(); // Paper - log detailed tick information - WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); -@@ -205,7 +205,7 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre - 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 - Rebrand - } - - 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 0000000000..c461807c43 --- /dev/null +++ b/purpur-server/paper-patches/features/0002-Ridables.patch @@ -0,0 +1,66 @@ +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 03ff6e062f92d0405dcf10bd35c6ba2fa553b8c0..4e584c69e3f13e7e441dc8a69d57287fc349841d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -1344,4 +1344,27 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + } + } + // Paper end - broadcast hurt animation ++ ++ // 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 1bb9a0bb4b1b898c9359d0095d9413a46b5e7630..1b7fdbecf9c28732d5196236980e87fa737a0769 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -602,6 +602,15 @@ public class CraftEventFactory { + // Paper end + craftServer.getPluginManager().callEvent(event); + ++ // Purpur start - Ridables ++ 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 - Ridables ++ + return event; + } + +@@ -1191,6 +1200,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 0000000000..372c10b756 --- /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 6d3f9d5dab6c9a2860ae31cae24310aa2d62da7c..4f29c579f94efe59a8c78520d75676fc4875e2f0 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java +@@ -145,8 +145,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 c6159c70f7a37b9bffe268b91905ce848d1d2927..d02adaaa6fbdc1c0eff44cb4a1f1642f9575a821 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +@@ -84,7 +84,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/patches/server/0052-Configurable-void-damage-height-and-damage.patch b/purpur-server/paper-patches/features/0004-Configurable-void-damage-height-and-damage.patch similarity index 66% rename from patches/server/0052-Configurable-void-damage-height-and-damage.patch rename to purpur-server/paper-patches/features/0004-Configurable-void-damage-height-and-damage.patch index cd6898609d..700ad9f281 100644 --- a/patches/server/0052-Configurable-void-damage-height-and-damage.patch +++ b/purpur-server/paper-patches/features/0004-Configurable-void-damage-height-and-damage.patch @@ -7,80 +7,39 @@ temporarily migrate to paper's config drop patch on the next minecraft release diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java -index c5644d8d64f12073e39bc6ed79c8714f4560ff89..47a2cba0db36b11548d06ec21f7c7d7c9a962d6e 100644 +index e48fa405d92fab221fa8331b65c8f324e801d439..e319d6337811051de478d584a37015c450960701 100644 --- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java -@@ -263,6 +263,7 @@ public class PaperConfigurations extends Configurations +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/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index 4e584c69e3f13e7e441dc8a69d57287fc349841d..762cd2f3a18bcb4039f8d232f1175aaac4ef623d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -260,6 +260,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + boolean retainPassengers = flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS); + // Don't allow teleporting between worlds while keeping passengers + if (flagSet.contains(io.papermc.paper.entity.TeleportFlag.EntityState.RETAIN_PASSENGERS) && this.entity.isVehicle() && location.getWorld() != this.getWorld()) { ++ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur - Add EntityTeleportHinderedEvent + 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 da365651d67f584547217701762564d74e09ad5d..9fbbdc9664353fd2be8eae112e5cfe8880d51d08 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1431,6 +1431,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + // Paper start - Teleport passenger API + // Don't allow teleporting between worlds while keeping passengers + if (ignorePassengers && entity.isVehicle() && location.getWorld() != this.getWorld()) { ++ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur - Add EntityTeleportHinderedEvent + return false; + } + +@@ -1452,6 +1453,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + } + + if (entity.isVehicle() && !ignorePassengers) { // Paper - Teleport API ++ if (!new org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent(entity.getBukkitEntity(), org.purpurmc.purpur.event.entity.EntityTeleportHinderedEvent.Reason.IS_VEHICLE, cause).callEvent()) // Purpur - Add EntityTeleportHinderedEvent + return false; + } + diff --git a/purpur-server/paper-patches/features/0006-API-for-any-mob-to-burn-daylight.patch b/purpur-server/paper-patches/features/0006-API-for-any-mob-to-burn-daylight.patch new file mode 100644 index 0000000000..ad8c5190d7 --- /dev/null +++ b/purpur-server/paper-patches/features/0006-API-for-any-mob-to-burn-daylight.patch @@ -0,0 +1,46 @@ +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 762cd2f3a18bcb4039f8d232f1175aaac4ef623d..8755fc950af21e076858de7aafe3b562982299cf 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -101,6 +101,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 459366331971d09e4cd00fb2035be01b4257477a..3a9d9b7526b2bf0fbd4e0d7886b3d849a6dcfee9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -1211,4 +1211,16 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + return this.getHandle().canUseSlot(org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot)); + } + // Paper end - Expose canUseSlot ++ ++ // 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/purpur-server/paper-patches/files/src/log4jPlugins/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java.patch b/purpur-server/paper-patches/files/src/log4jPlugins/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java.patch new file mode 100644 index 0000000000..5a006103d9 --- /dev/null +++ b/purpur-server/paper-patches/files/src/log4jPlugins/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java.patch @@ -0,0 +1,88 @@ +--- /dev/null ++++ b/src/log4jPlugins/java/org/purpurmc/purpur/gui/util/HighlightErrorConverter.java +@@ -1,0 +_,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/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 0000000000..fc55c48ff6 --- /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 +@@ -592,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()); // Pufferfish // Purpur - Purpur config files + + metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { + String minecraftVersion = Bukkit.getVersion(); +@@ -601,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 0000000000..bd41fc396a --- /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 + Charsets.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 0000000000..43b5c7a419 --- /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 0000000000..be4e59d898 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java.patch @@ -0,0 +1,13 @@ +--- a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java ++++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java +@@ -136,6 +_,10 @@ + static { + // TODO these kinda should be checked on each release, in case obfuscation changes + deobfuscationMap.put("abstract_skeleton_1", "abstract_skeleton_melee"); ++ // Purpur start - Add option to disable zombie aggressiveness towards villagers ++ deobfuscationMap.put("zombie_1", "zombie_attack_villager"); ++ deobfuscationMap.put("drowned_1", "drowned_attack_villager"); ++ // Purpur end - Add option to disable zombie aggressiveness towards villagers + + ignored.add("goal_selector_1"); + ignored.add("goal_selector_2"); 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 0000000000..e567d82a14 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java.patch @@ -0,0 +1,19 @@ +--- a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java ++++ b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java +@@ -61,6 +_,7 @@ + + // Follows CraftServer#getTPS + double[] tps = new double[] { ++ server.tps5s.getAverage(), // Purpur - Add 5 second tps average in /tps + server.tps1.getAverage(), + server.tps5.getAverage(), + server.tps15.getAverage() +@@ -73,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 0000000000..fd95c83648 --- /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 - Fix pufferfish issues // Purpur - Rebrand + getManifestAttribute(manifest, ATTRIBUTE_BRAND_NAME) +- .orElse(BRAND_PAPER_NAME), ++ .orElse(BRAND_PURPUR_NAME), // Purpur - Fix pufferfish issues // Purpur - Rebrand + SharedConstants.getCurrentVersion().getId(), + SharedConstants.getCurrentVersion().getName(), + 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/patches/server/0274-Improve-output-of-plugins-command.patch b/purpur-server/paper-patches/files/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java.patch similarity index 77% rename from patches/server/0274-Improve-output-of-plugins-command.patch rename to purpur-server/paper-patches/files/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java.patch index 2dfaa41958..eee6b84e6e 100644 --- a/patches/server/0274-Improve-output-of-plugins-command.patch +++ b/purpur-server/paper-patches/files/src/main/java/io/papermc/paper/command/PaperPluginsCommand.java.patch @@ -1,43 +1,33 @@ -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..865dc183276720d54d31d2a54d1bb5c845e80598 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 { +@@ -78,10 +_,10 @@ this.setAliases(Arrays.asList("pl")); } - private static List formatProviders(TreeMap> plugins) { -+ private static List formatProviders(TreeMap> plugins, @NotNull CommandSender sender) { // Purpur ++ private static List formatProviders(TreeMap> plugins, @NotNull CommandSender sender) { // Purpur - Improve output of plugins command List components = new ArrayList<>(plugins.size()); for (PluginProvider entry : plugins.values()) { - components.add(formatProvider(entry)); -+ components.add(formatProvider(entry, sender)); // Purpur ++ components.add(formatProvider(entry, sender)); // Purpur - Improve output of plugins command } boolean isFirst = true; -@@ -109,7 +109,7 @@ public class PaperPluginsCommand extends BukkitCommand { +@@ -109,7 +_,7 @@ return formattedSublists; } - private static Component formatProvider(PluginProvider provider) { -+ private static Component formatProvider(PluginProvider provider, @NotNull CommandSender sender) { // Purpur ++ private static Component formatProvider(PluginProvider provider, @NotNull CommandSender sender) { // Purpur - Improve output of plugins command TextComponent.Builder builder = Component.text(); if (provider instanceof SpigotPluginProvider spigotPluginProvider && CraftMagicNumbers.isLegacy(spigotPluginProvider.getMeta())) { builder.append(LEGACY_PLUGIN_STAR); -@@ -117,13 +117,65 @@ public class PaperPluginsCommand extends BukkitCommand { +@@ -117,13 +_,65 @@ String name = provider.getMeta().getName(); Component pluginName = Component.text(name, fromStatus(provider)) - .clickEvent(ClickEvent.runCommand("/version " + name)); -+ // Purpur start ++ // Purpur start - Improve output of plugins command + .clickEvent(ClickEvent.suggestCommand("/version " + name)); + + if (sender instanceof org.bukkit.entity.Player && sender.hasPermission("bukkit.command.version")) { @@ -71,14 +61,14 @@ index f0fce4113fb07c64adbec029d177c236cbdcbae8..865dc183276720d54d31d2a54d1bb5c8 + + pluginName.hoverEvent(hover.build()); + } -+ // Purpur end ++ // Purpur end - Improve output of plugins command builder.append(pluginName); return builder.build(); } -+ // Purpur start ++ // Purpur start - Improve output of plugins command + @NotNull + private static TextComponent getAuthors(@NotNull final PluginMeta pluginMeta) { + TextComponent.Builder builder = Component.text(); @@ -94,43 +84,45 @@ index f0fce4113fb07c64adbec029d177c236cbdcbae8..865dc183276720d54d31d2a54d1bb5c8 + + return builder.build(); + } -+ // Purpur end ++ // Purpur end - Improve output of plugins command + private static Component asPlainComponents(String strings) { net.kyori.adventure.text.TextComponent.Builder builder = Component.text(); for (String string : strings.split("\n")) { -@@ -182,24 +234,24 @@ public class PaperPluginsCommand extends BukkitCommand { +@@ -182,24 +_,24 @@ } } - 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); ++ //Component infoMessage = Component.text("Server Plugins (%s):".formatted(paperPlugins.size() + spigotPlugins.size()), NamedTextColor.WHITE); // Purpur - Improve output of plugins command //.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); - } -+ //if (!paperPlugins.isEmpty()) { // Purpur -+ sender.sendMessage(PAPER_HEADER.append(Component.text(" (%s):".formatted(paperPlugins.size())))); // Purpur -+ //} // Purpur - +- - for (Component component : formatProviders(paperPlugins)) { -+ for (Component component : formatProviders(paperPlugins, sender)) { // Purpur ++ //sender.sendMessage(infoMessage); // Purpur - Improve output of plugins command ++ ++ //if (!paperPlugins.isEmpty()) { // Purpur - Improve output of plugins command ++ sender.sendMessage(PAPER_HEADER.append(Component.text(" (%s):".formatted(paperPlugins.size())))); // Purpur - Improve output of plugins command ++ //} // Purpur - Improve output of plugins command ++ ++ for (Component component : formatProviders(paperPlugins, sender)) { // Purpur - Improve output of plugins command sender.sendMessage(component); } - if (!spigotPlugins.isEmpty()) { - sender.sendMessage(BUKKIT_HEADER); - } -+ //if (!spigotPlugins.isEmpty()) { // Purpur -+ sender.sendMessage(BUKKIT_HEADER.append(Component.text(" (%s):".formatted(spigotPlugins.size())))); // Purpur -+ //} // Purpur ++ //if (!spigotPlugins.isEmpty()) { // Purpur - Improve output of plugins command ++ sender.sendMessage(BUKKIT_HEADER.append(Component.text(" (%s):".formatted(spigotPlugins.size())))); // Purpur - Improve output of plugins command ++ //} // Purpur - Improve output of plugins command - for (Component component : formatProviders(spigotPlugins)) { -+ for (Component component : formatProviders(spigotPlugins, sender)) { // Purpur ++ for (Component component : formatProviders(spigotPlugins, sender)) { // Purpur - Improve output of plugins command sender.sendMessage(component); } diff --git a/patches/server/0177-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/0177-Enhance-SysoutCatcher.patch rename to purpur-server/paper-patches/files/src/main/java/io/papermc/paper/logging/SysoutCatcher.java.patch index 2a1703e678..d8d9954f69 100644 --- a/patches/server/0177-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/0199-Extended-OfflinePlayer-API.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java.patch similarity index 79% rename from patches/server/0199-Extended-OfflinePlayer-API.patch rename to purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java.patch index 721d5726b3..e6ecfddea5 100644 --- a/patches/server/0199-Extended-OfflinePlayer-API.patch +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java.patch @@ -1,22 +1,14 @@ -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 94ca0407303c4493ab4928b12ec6ecc75aaca549..a138e1b6b66d99f2035de054137a607aa6b7f0b9 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java -@@ -363,14 +363,26 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa +@@ -363,14 +_,26 @@ @Override public Location getLocation() { -+ // Purpur start ++ // Purpur start - OfflinePlayer API + if (this.isOnline()) { + return this.getPlayer().getLocation(); + } -+ // Purpur end ++ // Purpur end - OfflinePlayer API + CompoundTag data = this.getData(); if (data == null) { @@ -38,7 +30,7 @@ index 94ca0407303c4493ab4928b12ec6ecc75aaca549..a138e1b6b66d99f2035de054137a607a UUID uuid = new UUID(data.getLong("WorldUUIDMost"), data.getLong("WorldUUIDLeast")); -@@ -381,9 +393,9 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa +@@ -381,9 +_,9 @@ rotation.getFloat(0), rotation.getFloat(1) ); @@ -50,7 +42,7 @@ index 94ca0407303c4493ab4928b12ec6ecc75aaca549..a138e1b6b66d99f2035de054137a607a } @Override -@@ -626,4 +638,191 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa +@@ -626,4 +_,191 @@ manager.save(); } } @@ -242,36 +234,3 @@ index 94ca0407303c4493ab4928b12ec6ecc75aaca549..a138e1b6b66d99f2035de054137a607a + } + // 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 ea8b1f01b2dd626c422649488c1e605a8054dbf2..da20ebe8858b4ab5bf8ac62aeac1942320bfa91c 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -2769,6 +2769,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 0000000000..7a5ade3e0a --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch @@ -0,0 +1,154 @@ +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -427,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()); + +@@ -1087,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 && config.spawnMonsters); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) +@@ -1102,6 +_,7 @@ + } + } + world.spigotConfig.init(); // Spigot ++ world.purpurConfig.init(); // Purpur - Purpur config files + } + + Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper +@@ -1119,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"); + +@@ -1646,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) -> clearBlockHighlights()); ++ } ++ // Purpur end - Debug Marker API ++ + @Override + public List getRecipesFor(ItemStack result) { + Preconditions.checkArgument(result != null, "ItemStack cannot be null"); +@@ -3055,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() { + org.spigotmc.RestartCommand.restart(); +@@ -3084,6 +_,7 @@ + @Override + public double[] getTPS() { + return new double[] { ++ net.minecraft.server.MinecraftServer.getServer().tps5s.getAverage(), // Purpur - Add 5 second tps average in /tps + net.minecraft.server.MinecraftServer.getServer().tps1.getAverage(), + net.minecraft.server.MinecraftServer.getServer().tps5.getAverage(), + net.minecraft.server.MinecraftServer.getServer().tps15.getAverage() +@@ -3294,4 +_,18 @@ + this.console.addPluginAllowingSleep(plugin.getName(), value); + } + // Paper end - API to check if the server is sleeping ++ ++ // 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 0000000000..6fa5de9712 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftWorld.java.patch @@ -0,0 +1,53 @@ +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -2349,6 +_,50 @@ + 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(io.papermc.paper.util.MCUtil.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) { ++ 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 - Debug Marker API ++ + @Override + public Collection getStructures(int x, int z) { + return this.getStructures(x, z, struct -> true); diff --git a/patches/server/0022-Disable-outdated-build-check.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/Main.java.patch similarity index 51% rename from patches/server/0022-Disable-outdated-build-check.patch rename to purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/Main.java.patch index b986551de6..e86001e4bf 100644 --- a/patches/server/0022-Disable-outdated-build-check.patch +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/Main.java.patch @@ -1,19 +1,25 @@ -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 d313f3a9b31d4ecc3b48f8fc2e44d3b445e8e2c5..b8cacfbd6f8f3a37db21586f6febb55d42170e5b 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -266,7 +266,7 @@ public class Main { +@@ -176,6 +_,13 @@ + .describedAs("Jar file"); + // Paper end + ++ // Purpur start - Purpur config files ++ 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 + // Paper start + acceptsAll(asList("server-name"), "Name of the server") + .withRequiredArg() +@@ -259,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 0000000000..39590b5cd9 --- /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 tileEntity) { + super(world, tileEntity); ++ // Purpur start - load bees to be able to modify them individually - Stored Bee API ++ for(BeehiveBlockEntity.BeeData data : tileEntity.getStored()) { ++ storage.add(new org.purpurmc.purpur.entity.PurpurStoredBee(data, this)); ++ } ++ // 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.getTileEntityFromWorld()); ++ 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)); ++ } ++ // 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 0000000000..e60687bc81 --- /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 +@@ -73,7 +_,7 @@ + public int getRange() { + this.ensureNoWorldGeneration(); + ConduitBlockEntity conduit = (ConduitBlockEntity) this.getTileEntityFromWorld(); +- 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 0000000000..411f8ce59a --- /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 +@@ -91,7 +_,7 @@ + // 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 - Rebrand + } + + @Override 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 0000000000..195b6492ac --- /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 +@@ -21,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 + } + // Paper start + @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 0000000000..82e4f1b762 --- /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 +@@ -89,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 0000000000..7217129156 --- /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 +@@ -281,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 0000000000..d09a7cf6b3 --- /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 +@@ -27,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 0000000000..8625ef963e --- /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 +@@ -151,4 +_,53 @@ + public String toString() { + return "CraftItem"; + } ++ ++ // 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 0000000000..defd314c01 --- /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 +@@ -523,7 +_,7 @@ + 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 - Config for mob last hurt by player time + } + // Paper end + 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 0000000000..57cca6a076 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java.patch @@ -0,0 +1,19 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java +@@ -90,4 +_,16 @@ + 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 0000000000..0853c28488 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch @@ -0,0 +1,123 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -585,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 : (List) this.server.getHandle().players) { + if (player.getBukkitEntity().canSee(this)) { +@@ -2745,6 +_,28 @@ + 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); + } +@@ -3541,4 +_,74 @@ + this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundEntityEventPacket(((CraftEntity) target).getHandle(), effect.getData())); + } + // Paper end - entity effect API ++ // Purpur start - Purpur client support ++ @Override ++ public boolean usesPurpurClient() { ++ return getHandle().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; ++ this.getHandle().connection.send(new net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket(new net.minecraft.network.protocol.common.custom.GameTestAddMarkerDebugPayload(io.papermc.paper.util.MCUtil.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; ++ 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 + } 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 0000000000..44a2a5b99f --- /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 +@@ -28,4 +_,17 @@ + public String toString() { + return "CraftSnowman"; + } ++ ++ // 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 0000000000..9f16640c49 --- /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 +@@ -373,4 +_,11 @@ + getHandle().getGossips().gossips.clear(); + } + // Paper end ++ ++ // 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 0000000000..d1bf807f14 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java.patch @@ -0,0 +1,20 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java +@@ -99,4 +_,17 @@ + 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 0000000000..a85fb1dbd9 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java.patch @@ -0,0 +1,18 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java +@@ -145,4 +_,15 @@ + return this.getKey().hashCode(); + } + } ++ // 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 + } diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch new file mode 100644 index 0000000000..ce3a906ab3 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -1131,7 +_,7 @@ + return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.LAVA, bukkitDamageSource, modifiers, modifierFunctions, cancelled); + } else if (source.getDirectBlock() != null) { + DamageCause cause; +- if (source.is(DamageTypes.CACTUS) || source.is(DamageTypes.SWEET_BERRY_BUSH) || source.is(DamageTypes.STALAGMITE) || source.is(DamageTypes.FALLING_STALACTITE) || source.is(DamageTypes.FALLING_ANVIL)) { ++ if (source.is(DamageTypes.CACTUS) || source.is(DamageTypes.SWEET_BERRY_BUSH) || source.is(DamageTypes.STALAGMITE) || source.is(DamageTypes.FALLING_STALACTITE) || source.is(DamageTypes.FALLING_ANVIL) || source.isStonecutter()) { // Purpur - Stonecutter damage + cause = DamageCause.CONTACT; + } else if (source.is(DamageTypes.HOT_FLOOR)) { + cause = DamageCause.HOT_FLOOR; 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 0000000000..58b895e593 --- /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/patches/server/0291-ItemStack-convenience-methods.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java.patch similarity index 94% rename from patches/server/0291-ItemStack-convenience-methods.patch rename to purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java.patch index d60ec7faeb..458f702dd8 100644 --- a/patches/server/0291-ItemStack-convenience-methods.patch +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java.patch @@ -1,19 +1,11 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Thu, 3 Oct 2024 18:33:14 -0700 -Subject: [PATCH] ItemStack convenience methods - - -diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java -index 78975412da0f0c2b802bfce6d30d56b26d8023e2..4ec6a07796023aab2f8f84f131f48108c235c852 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java -@@ -658,4 +658,285 @@ public final class CraftItemStack extends ItemStack { +@@ -666,4 +_,285 @@ } // Paper end - data component API + -+ // Purpur start ++ // Purpur start - ItemStack convenience methods + @Override + public String getDisplayName() { + return getItemMeta().getDisplayName(); @@ -292,5 +284,5 @@ index 78975412da0f0c2b802bfce6d30d56b26d8023e2..4ec6a07796023aab2f8f84f131f48108 + } + return random.nextInt(unbreaking + 1) > 0; + } -+ // Purpur end ++ // 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 0000000000..b3042e1f54 --- /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 0000000000..f2659f49e3 --- /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 +@@ -75,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 0000000000..9061089d2d --- /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 +@@ -265,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/patches/server/0289-Adopt-MaterialRerouting.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java.patch similarity index 69% rename from patches/server/0289-Adopt-MaterialRerouting.patch rename to purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java.patch index 49463aba89..74a3592447 100644 --- a/patches/server/0289-Adopt-MaterialRerouting.patch +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java.patch @@ -1,20 +1,10 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: granny -Date: Thu, 13 Jun 2024 16:00:30 -0700 -Subject: [PATCH] Adopt MaterialRerouting - -Adopts the purpur-api to the material rerouting infrastructure introduced -by upstream's upstream. - -diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java -index db8d8e2a07296d62c3097f02b03319e2e1ba9394..e5c30847297e056782084d81fb9300f98d4a8f75 100644 --- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java +++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java -@@ -708,4 +708,32 @@ public class MaterialRerouting { +@@ -708,4 +_,32 @@ meta.setCanPlaceOn(materials); } // Paper end -+ // Purpur start ++ // 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); @@ -41,5 +31,5 @@ index db8d8e2a07296d62c3097f02b03319e2e1ba9394..e5c30847297e056782084d81fb9300f9 + public static BlockData getBlockData(ItemStack itemStack, Material material) { + return itemStack.getBlockData(MaterialRerouting.transformToBlockType(material)); + } -+ // Purpur end ++ // 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 0000000000..c9e34b3add --- /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 +@@ -49,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 0000000000..46d23c4170 --- /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/CraftMagicNumbers.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java.patch new file mode 100644 index 0000000000..2f747707b9 --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -508,7 +_,7 @@ + // Paper start + @Override + public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { +- return new com.destroystokyo.paper.PaperVersionFetcher(); ++ return new com.destroystokyo.paper.PaperVersionFetcher(); // Pufferfish // Purpur - Rebrand + } + + @Override diff --git a/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/Versioning.java.patch b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/Versioning.java.patch new file mode 100644 index 0000000000..a560ff72ac --- /dev/null +++ b/purpur-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/Versioning.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java +@@ -11,7 +_,7 @@ + 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/org.purpurmc.purpur/purpur-api/pom.properties"); // Pufferfish // Purpur - Rebrand + Properties properties = new Properties(); + + if (stream != null) { 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 0000000000..6106785b3e --- /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 0000000000..423a402061 --- /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 +@@ -43,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 0000000000..03e3a05ad6 --- /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); // Paper +@@ -77,14 +_,14 @@ + if (isLongTimeout) { + // Paper end + 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, "------------------------------"); +@@ -104,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.getId(), Integer.MAX_VALUE), logger); + logger.log(Level.SEVERE, "------------------------------"); +@@ -122,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 0000000000..8b4a310325 --- /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/0237-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 68% rename from patches/server/0237-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 d7d2fd0a07..6fb4710797 100644 --- a/patches/server/0237-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 75ed5050f72c001d6eab117a2c0b352a413548bd..180c0a532bbac10a8280b63eb7aa783a1bfbb237 100644 --- a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java +++ b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java -@@ -46,6 +46,7 @@ public class MinecraftCommandPermissionsTest { +@@ -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 { -@@ -58,6 +59,25 @@ public class MinecraftCommandPermissionsTest { +@@ -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 75ed5050f72c001d6eab117a2c0b352a413548bd..180c0a532bbac10a8280b63eb7aa783a + "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/main/java/org/purpurmc/purpur/PurpurConfig.java b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java new file mode 100644 index 0000000000..702f71bed6 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurConfig.java @@ -0,0 +1,602 @@ +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.ResourceLocation; +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", 38); + set("config-version", 38); + + 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 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); + 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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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 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 0000000000..22e793c40f --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java @@ -0,0 +1,3447 @@ +package org.purpurmc.purpur; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.function.Predicate; +import java.util.logging.Level; +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.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.lang.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) { + 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 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)); + } + + 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; + } + + 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 entitiesPickUpLootBypassMobGriefing = false; + public boolean fireballsBypassMobGriefing = false; + public boolean projectilesBypassMobGriefing = false; + 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; + 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); + entitiesPickUpLootBypassMobGriefing = getBoolean("gameplay-mechanics.entities-pick-up-loot-bypass-mob-griefing", entitiesPickUpLootBypassMobGriefing); + fireballsBypassMobGriefing = getBoolean("gameplay-mechanics.fireballs-bypass-mob-griefing", fireballsBypassMobGriefing); + projectilesBypassMobGriefing = getBoolean("gameplay-mechanics.projectiles-bypass-mob-griefing", projectilesBypassMobGriefing); + 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); + } + + 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 boolean explosionClampRadius = true; + private void explosionSettings() { + explosionClampRadius = getBoolean("gameplay-mechanics.clamp-explosion-radius", explosionClampRadius); + } + + 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 ResourceLocation dontRunWithScissorsItemModelReference = ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.parse(key.toString())); + if (item != Items.AIR) itemImmuneToLightning.add(item); + }); + dontRunWithScissors = getBoolean("gameplay-mechanics.item.shears.damage-if-sprinting", dontRunWithScissors); + dontRunWithScissorsItemModelReference = ResourceLocation.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(ResourceLocation.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(ResourceLocation.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 < 38) { + 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())); + } + 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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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()))) + ).forEach((blockId, obj) -> { + Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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()))) + ).forEach((blockId, obj) -> { + Block block = BuiltInRegistries.BLOCK.getValue(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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(ResourceLocation.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 farmlandBypassMobGriefing = false; + 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() { + 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); + 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 powderSnowBypassMobGriefing = false; + private void powderSnowSettings() { + 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; + 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 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; + 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 = 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); + 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); + 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 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 creeperBypassMobGriefing = false; + 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); + 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); + 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 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); + 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); + 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; + 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 endermanBypassMobGriefing = false; + 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); + 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); + 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 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); + 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); + 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; + 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 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); + 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); + 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; + 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 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 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 piglinBypassMobGriefing = false; + 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); + 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); + 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 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); + 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); + 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; + 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(ResourceLocation.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 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); + 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); + 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; + public boolean ravagerRidableInWater = false; + public boolean ravagerControllable = true; + public double ravagerMaxHealth = 100.0D; + public double ravagerScale = 1.0D; + public boolean ravagerBypassMobGriefing = false; + 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); + 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.getValue(ResourceLocation.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 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); + 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); + 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; + 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 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); + 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); + 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; + 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 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); + 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); + 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; + 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 villagerBypassMobGriefing = false; + 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); + 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); + 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; + 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; + 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 witherBypassMobGriefing = false; + 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); + 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); + 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 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); + 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); + 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 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 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 = 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); + 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); + 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(ResourceLocation.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 0000000000..79b8490832 --- /dev/null +++ b/purpur-server/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/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 0000000000..40d2fab4a9 --- /dev/null +++ b/purpur-server/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/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 0000000000..235f3cd89f --- /dev/null +++ b/purpur-server/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/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 0000000000..74a602c331 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/command/PingCommand.java @@ -0,0 +1,32 @@ +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 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.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 0000000000..7163c8247c --- /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 0000000000..2852c07adb --- /dev/null +++ b/purpur-server/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/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 0000000000..992f8dfc62 --- /dev/null +++ b/purpur-server/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/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 0000000000..d8f9b04410 --- /dev/null +++ b/purpur-server/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/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 0000000000..4bb475099b --- /dev/null +++ b/purpur-server/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/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 0000000000..940bcc6f79 --- /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 0000000000..e0bbaec05a --- /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 0000000000..dd21951815 --- /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().broadcast(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 0000000000..34f3c43fa1 --- /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 0000000000..922e48799c --- /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 0000000000..7608bf0981 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/entity/PurpurStoredBee.java @@ -0,0 +1,106 @@ +package org.purpurmc.purpur.entity; + +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.item.component.CustomData; +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; + + CompoundTag customData = handle.occupant.entityData().copyTag(); + this.customName = customData.contains("CustomName") + ? PaperAdventure.asAdventure(net.minecraft.network.chat.Component.Serializer.fromJson(customData.getString("CustomName"), MinecraftServer.getDefaultRegistryAccess())) + : null; + + if(customData.contains("BukkitValues", Tag.TAG_COMPOUND)) { + this.persistentDataContainer.putAll(customData.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.occupant.entityData().copyTag().put("BukkitValues", this.persistentDataContainer.toTagCompound()); + if(customName == null) { + handle.occupant.entityData().copyTag().remove("CustomName"); + } else { + handle.occupant.entityData().copyTag().putString("CustomName", net.minecraft.network.chat.Component.Serializer.toJson(PaperAdventure.asVanilla(customName), MinecraftServer.getDefaultRegistryAccess())); + } + } +} 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 0000000000..8babdaddd8 --- /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 0000000000..432f4f3d82 --- /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.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/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 0000000000..18a95e043c --- /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.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/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 0000000000..9660716f41 --- /dev/null +++ b/purpur-server/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/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 0000000000..8ffacde041 --- /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; +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, + false, true, + 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 0000000000..580d8dc556 --- /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, + false, true, + 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.isInWaterOrBubble()) { + 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 0000000000..550222758b --- /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 0000000000..d75fb5e77e --- /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 0000000000..b257f35caa --- /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 0000000000..ed50cb2115 --- /dev/null +++ b/purpur-server/src/main/java/org/purpurmc/purpur/item/SpawnerItem.java @@ -0,0 +1,40 @@ +package org.purpurmc.purpur.item; + +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(); + if (customData.contains("Purpur.mob_type")) { + EntityType.byString(customData.getString("Purpur.mob_type")).ifPresent(type -> spawner.getSpawner().setEntityId(type, level, level.random, pos)); + } else if (customData.contains("Purpur.SpawnData")) { + net.minecraft.world.level.SpawnData.CODEC.parse(net.minecraft.nbt.NbtOps.INSTANCE, customData.getCompound("Purpur.SpawnData")).result() + .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 0000000000..793a3ea45f --- /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.ResourceLocation; +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<>(ResourceLocation.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 0000000000..fa72769e06 --- /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.ResourceLocation; +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<>(ResourceLocation.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 0000000000..664f9d5e1c --- /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 0000000000..3c3d4cd52d --- /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 0000000000..bece7eefc8 --- /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 0000000000..8e98c0ae73 --- /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 0000000000..8769993e7c --- /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 0000000000..e18c37f067 --- /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 0000000000..345d4ee4ff --- /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 0000000000..bf5402214f --- /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 0000000000..715f6dd444 --- /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 0000000000..64adb13b29 --- /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 0000000000..b7586f4945 --- /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 0000000000..77a18f3048 --- /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/settings.gradle.kts b/settings.gradle.kts index 0ff9678cd8..ce2ad53be2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,7 @@ pluginManagement { } plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" } if (!file(".git").exists()) { @@ -32,7 +32,7 @@ if (!file(".git").exists()) { } rootProject.name = "purpur" -for (name in listOf("Purpur-API", "Purpur-Server", "paper-api-generator")) { +for (name in listOf("purpur-api", "purpur-server", "purpur-api-generator")) { val projName = name.lowercase(Locale.ENGLISH) include(projName) findProject(":$projName")!!.projectDir = file(name)