diff --git a/.github/workflows/ver1193.yml b/.github/workflows/ver1193.yml new file mode 100644 index 00000000..0e1ae5ef --- /dev/null +++ b/.github/workflows/ver1193.yml @@ -0,0 +1,30 @@ +name: Build Leaf 1.19.3 +on: [ push, pull_request ] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: gradle/wrapper-validation-action@master + - name: Set up JDK + uses: graalvm/setup-graalvm@v1 + with: + version: "latest" + java-version: 19 + github-token: "${{ secrets.GITHUB_TOKEN }}" + cache: gradle + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - 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 -Dorg.gradle.jvmargs="-Dgraal.CompilerConfiguration=enterprise -Dgraal.UsePriorityInlining=true -Dgraal.Vectorization=true -Dgraal.OptDuplication=true --add-modules jdk.incubator.vector" applyPatches --stacktrace --no-daemon + - name: Create ReobfPaperclipJar + run: ./gradlew -Dorg.gradle.jvmargs="-Dgraal.CompilerConfiguration=enterprise -Dgraal.UsePriorityInlining=true -Dgraal.Vectorization=true -Dgraal.OptDuplication=true --add-modules jdk.incubator.vector" createReobfPaperclipJar --stacktrace --no-daemon + - name: Rename Paperclip Jar + run: mv build/libs/leaf-paperclip-1.19.3-R0.1-SNAPSHOT-reobf.jar ./leaf-1.19.3.jar + - name: Upload Leaf + uses: actions/upload-artifact@v3 + with: + name: Leaf + path: ./leaf-1.19.3.jar diff --git a/.gitignore b/.gitignore index a1fc39c0..d723d361 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,59 @@ -.gradle -/build/ +# JVM crash related +core.* +hs_err_pid* -# Ignore Gradle GUI config -gradle-app.setting +# Intellij +.idea/ +*.iml +*.ipr +*.iws +out/ -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +# Eclipse +.classpath +.project +.settings/ + +# netbeans +nbproject/ +nbactions.xml + +# Gradle !gradle-wrapper.jar +.gradle/ +build/ +*/build/ -# Cache of project -.gradletasknamecache +# we use maven! +build.xml -# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 -# gradle/wrapper/gradle-wrapper.properties +# Maven +log/ +target/ +dependency-reduced-pom.xml + +# various other potential build files +build/ +bin/ +dist/ +manifest.mf + +# Mac +.DS_Store/ +.DS_Store + +# vim +.*.sw[a-p] + +# Linux temp files +*~ + +# other stuff +run/ + +build-data/ +Leaf-API +Leaf-MojangAPI +Leaf-Server +*.jar +/patches/todo/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..3b9622b0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,449 @@ +Contributing to Leaf +========================== +Leaf is happy you're willing to contribute to our projects. We are usually +very lenient with all submitted PRs, but there are still some guidelines you +can follow to make the approval process go more smoothly. + +## Use a Personal Fork and not Organization + +Leaf will routinely modify your PR, whether it's a quick rebase or to take care +of any minor nitpicks we might have. Often, it's better for us to solve these +problems for you than make you go back and forth trying to fix it yourself. + +Unfortunately, if you use an organization for your PR, it prevents Leaf 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. + +## Requirements + +To get started with PRing changes, you'll need the following software, most of +which can be obtained in (most) package managers such as `apt` (Debian / Ubuntu; +you will most likely use this for WSL), `homebrew` (macOS / Linux), and more: + +- `git` (package `git` everywhere); +- A Java 17 or later JDK (packages vary, use Google/DuckDuckGo/etc.). + - [Adoptium](https://adoptium.net/) has builds for most operating systems. + - Paper requires JDK 17 to build, however makes use of Gradle's + [Toolchains](https://docs.gradle.org/current/userguide/toolchains.html) + feature to allow building with only JRE 8 or later installed. (Gradle will + automatically provision JDK 17 for compilation if it cannot find an existing + install). + +If you're on Windows, check +[the section on WSL](#patching-and-building-is-really-slow-what-can-i-do). + +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:17.0.1_12-jdk bash +Pulling image... + +root@abcdefg1234:/# javac -version +javac 17.0.1 +``` + +## Understanding Patches + +Leaf is mostly patches and extensions to Pufferfish. These patches/extensions are +split into different directories which target certain parts of the code. These +directories are: + +- `Leaf-API` - Modifications to `Pufferfish-API`; +- `Leaf-Server` - Modifications to `Pufferfish`. + +Because the entire structure is based on patches and git, a basic understanding +of how to use git is required. A basic tutorial can be found here: +. + +Assuming you have already forked the repository: + +1. Clone your fork to your local machine; +2. Type `./gradlew applyPatches` in a terminal to apply the changes from upstream. +On Windows, leave out the `./` at the beginning for all `gradlew` commands; +3. cd into `Leaf-Server` for server changes, and `Leaf-API` for API changes. + + +`Leaf-Server` and `Leaf-API` aren't git repositories in the traditional sense: + +- `base` points to the unmodified source before Leaf patches have been applied. +- Each commit after `base` is a patch. + +## Adding Patches + +Adding patches to Leaf is very simple: + +1. Modify `Leaf-Server` and/or `Leaf-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. + +Your commit will be converted into a patch that you can then PR into Leaf. + +> ❗ 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 Patches + +Modifying previous patches is a bit more complex: + +### Method 1 + +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 +later; + - You can type `git stash pop` to get them back at any point. +1. Type `git rebase -i base`; + - It should show something like + [this](https://gist.github.com/zachbr/21e92993cb99f62ffd7905d7b02f3159) in + the text editor you get. + - If your editor does not have a "menu" at the bottom, you're using `vim`. + 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 +"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. + +### Method 2 - Fixup commits + +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. + +This method has the benefit of being able to compile to test your change without +messing with your HEADs. + +#### Manual method + +1. Make your change while at HEAD; +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; +1. Change the `pick` to the appropriate action: + 1. `f`/`fixup`: Merge your changes into the patch without touching the + 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; + - 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 `; + - You can also use `--squash` instead of `--fixup` if you want the commit + message to also be changed. + - You can get the hash by looking at `git log` or `git blame`; your IDE can + assist you too. + - Alternatively, if you only know the name of the patch, you can do + `git commit -a --fixup "Subject of Patch name"`. +1. Rebase with autosquash: `git rebase -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. + +## 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 Leaf 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. Apply updated patches: `./gradlew applyPatches`. +1. If there are conflicts, fix them. +1. If your PR creates new patches instead of modifying exist ones, in both the `Leaf-Server` and `Leaf-API` directories, 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`. + +## PR Policy + +We'll accept changes that make sense. You should be able to justify their +existence, along with any maintenance costs that come with them. Using +[obfuscation helpers](#obfuscation-helpers) aids in the maintenance costs. +Remember that these changes will affect everyone who runs Leaf, not just you +and your server. + +While we will fix minor formatting issues, you should stick to the guide below +when making and submitting changes. + +## Formatting + +All modifications to non-Leaf files should be marked. + +- Multi-line changes start with `// Leaf start` and end with `// Leaf end`; +- You can put a comment with an explanation if it isn't obvious, like this: +`// Leaf start - reason`. + - The comments should generally be about the reason the change was made, what + it was before, or what the change is. + - Multi-line messages should start with `// Leaf start` and use `/* Multi + line message here */` for the message itself. +- One-line changes should have `// Leaf` or `// Leaf - reason`. + +Here's an example of how to mark changes by Leaf: + +```java +entity.getWorld().dontbeStupid(); // Leaf - was beStupid() which is bad +entity.getFriends().forEach(Entity::explode); +entity.a(); +entity.b(); +// Leaf start - use plugin-set spawn +// entity.getWorld().explode(entity.getWorld().getSpawn()); +Location spawnLocation = ((CraftWorld)entity.getWorld()).getSpawnLocation(); +entity.getWorld().explode(new BlockPosition(spawnLocation.getX(), spawnLocation.getY(), spawnLocation.getZ())); +// Leaf end +``` + +We generally follow usual Java style (aka. Oracle style), or what is programmed +into most IDEs and formatters by default. There are a few notes, however: +- It is fine to go over 80 lines as long as it doesn't hurt readability. +There are exceptions, especially in Spigot-related files +- When in doubt or the code around your change is in a clearly different style, +use the same style as the surrounding code. + +## Patch Notes + +When submitting patches to Leaf, we may ask you to add notes to the patch +header. While we do not require it for all changes, you should add patch notes +when the changes you're making are technical, complex, or require an explanation +of some kind. It is very likely that your patch will remain long after we've all +forgotten about the details of your PR; patch notes will help us maintain it +without having to dig back through GitHub history looking for your PR. + +These notes should express the intent of your patch, as well as any pertinent +technical details we should keep in mind long-term. Ultimately, they exist to +make it easier for us to maintain the patch across major version changes. + +If you add a message to your commit in the `Leaf-Server`/`Leaf-API` +directories, the rebuild patches script will handle these patch notes +automatically as part of generating the patch file. If you are not +extremely careful, you should always just `squash` or `amend` a patch (see the +above sections on modifying patches) and rebuild. + +Editing messages and patches by hand is possible, but you should patch and +rebuild afterwards to make sure you did it correctly. This is slower than just +modifying the patches properly after a few times, so you will not really gain +anything but headaches from doing it by hand. + +Underneath is an example patch header/note: + +```patch +From 02abc033533f70ef3165a97bfda3f5c2fa58633a Mon Sep 17 00:00:00 2001 +From: Shane Freeder +Date: Sun, 15 Oct 2017 00:29:07 +0100 +Subject: [PATCH] revert serverside behavior of keepalives + +This patch intends to bump up the time that a client has to reply to the +server back to 30 seconds as per pre 1.12.2, which allowed clients +more than enough time to reply potentially allowing them to be less +tempermental due to lag spikes on the network thread, e.g. that caused +by plugins that are interacting with netty. + +We also add a system property to allow people to tweak how long the server +will wait for a reply. There is a compromise here between lower and higher +values, lower values will mean that dead connections can be closed sooner, +whereas higher values will make this less sensitive to issues such as spikes +from networking or during connections flood of chunk packets on slower clients, + at the cost of dead connections being kept open for longer. + +diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java +index a92bf8967..d0ab87d0f 100644 +--- a/src/main/java/net/minecraft/server/PlayerConnection.java ++++ b/src/main/java/net/minecraft/server/PlayerConnection.java +``` + +## Obfuscation Helpers + +While rarely needed, obfuscation helpers are sometimes useful when it comes +to unmapped local variables, or poorly named method parameters. In an effort +to make future updates easier on ourselves, Leaf tries to use obfuscation +helpers wherever it makes sense. The purpose of these helpers is to make the +code more readable and maintainable. These helpers should be made easy to +inline by the JVM wherever possible. + +An example of an obfuscation helper for a local variable: +```java +double d0 = entity.getX(); final double fromX = d0; // Leaf - OBFHELPER +// ... +this.someMethod(fromX); // Leaf +``` + +While they may not always be done in exactly the same way, the general goal is +always to improve readability and maintainability. Use your best judgment and do +what fits best in your situation. + +## Configuration files + +To use a configurable value in your patch, add a new field in either the +`GlobalConfiguration` or `WorldConfiguration` classes (inside the +`io.papermc.paper.configuration` package). Use `GlobalConfiguration` if a value +must remain the same throughout all worlds, or the latter if it can change +between worlds. World-specific configuration options are preferred whenever +possible. + +### Example +This is adding a new miscellaneous setting that doesn't seem to fit in other categories. +Try to check and see if an existing category (inner class) exists that matches +whatever configuration option you are adding. +```java +public class GlobalConfiguration { + // other sections + public class Misc extends ConfigurationPart { + // other settings + public boolean lagCompensateBlockBreaking = true; + public boolean useDimensionTypeForCustomSpawners = false; + public int maxNumOfPlayers = 20; // This is the new setting + } +} +``` +You set the type of the setting as the field type, and the default value is the +initial field value. The name of the setting defaults to the snake-case of the +field name, so in this case it would be `misc.max-num-of-players`. You can use +the `@Setting` annotation to override that, but generally just try to set the +field name to what you want the setting to be called. + +#### Accessing the value +If you added a new global config value, you can access it in the code just by +doing +```java +int maxPlayers = GlobalConfiguration.get().misc.maxNumOfPlayers; +``` +Generally for global config values you will use the fully qualified class name, +`io.papermc.paper.configuration.GlobalConfiguration` since it's not imported in +most places. +--- +If you are adding a new world config value, you must have access to an instance +of the `net.minecraft.world.level.Level` which you can then access the config by doing +```java +int maxPlayers = level.paperConfig().misc.maxNumOfPlayers; +``` + +#### Committing changes +All changes to the `GlobalConfiguration` and `WorldConfiguration` 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 Leaf Test Plugin + +The Leaf project has a `test-plugin` module for easily testing out API changes +and additions. To use the test plugin, enable it in `test-plugin.settings.gradle.kts`, +which will be generated after running Gradle at least once. After this, you can edit +the test plugin, and run a server with the plugin using `./gradlew runDev` (or any +of the other Leaf run tasks). + +### Publishing to Maven local (use in external plugins) + +To build and install the Leaf APIs and Server to your local Maven repository, do the following: + +- Run `./gradlew publishToMavenLocal` in the base directory. + +If you use Gradle to build your plugin: +- Add `mavenLocal()` as a repository. Gradle checks repositories in the order they are declared, + so if you also have the Leaf repository added, put the local repository above Leaf's. +- Make sure to remove `mavenLocal()` when you are done testing, see the [Gradle docs](https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:case-for-maven-local) + for more details. + +If you use Maven to build your plugin: +- If you later need to use the Leaf-API, you might want to remove the jar + from your local Maven repository. + If you use Windows and don't usually build using WSL, you might not need to + do this. + +## Frequently Asked Questions + +### I can't find the NMS file I need! + +By default, Leaf (and upstream) only import files we make changes to. If you +would like to make changes to a file that isn't present in `Leaf-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 + `./Leaf-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. + +This case most often applies to changes to files like `README.md`, this very +file (`CONTRIBUTING.md`), the `LICENSE.md` file, and so forth. + +### Patching and building is *really* slow, what can I do? + +This only applies if you're running Windows. If you're running a prior Windows +release, either update to Windows 10/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). + +To set up WSL 2, follow the information here: + + +You will most likely want to use the Ubuntu apps. Once it's set up, install the +required tools with `sudo apt-get update && sudo apt-get install $TOOL_NAMES +-y`. Replace `$TOOL_NAMES` with the packages found in the +[requirements](#requirements). You can now clone the repository and do +everything like usual. + +> ❗ Do not use the `/mnt/` directory in WSL! Instead, mount the WSL directories +> in Windows like described here: +> + +[MappingViewer]: https://nms.screamingsandals.org/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..6c40a956 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +The MIT License (MIT) +===================== + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/Leaf.png b/Leaf.png new file mode 100644 index 00000000..1ee4589b Binary files /dev/null and b/Leaf.png differ diff --git a/README.md b/README.md index 8d57d807..ccfc0d6a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,58 @@ -# Leaf -Personal fork of Purpur, A Minecraft Server Software for Winds Network +Leaf logo +
+ +## Leaf + +[![Github Actions Build](https://img.shields.io/github/actions/workflow/status/Winds-Studio/Leaf/main.yml?branch=main&style=flat-square)](https://github.com/Winds-Studio/Leaf/actions) +[![MIT License](https://img.shields.io/github/license/Dreeam-qwq/Leaf?style=flat-square)](LICENSE) + +
Leaf is a drop-in replacement for Pufferfish servers designed for fix some bugs and customize, and performance built on top of Pufferfish.
+Logo created by NaixiNana +
+ +## Features + - Fork of [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) for better performance. + - Allowing all characters as username, including Chinese and other characters. + - Allowing players connect to backend server through proxy without enabling bunngecord mode. + - Allowing players use tripwire dupe. + - Disabling the UseItemOnPacket Too Far check. + - Update all dependencies to the latest. + - Some Purpur patches. + - ... + +## Contact + +- 📫 Discord: `Dreeam#0851` | QQ: `2682173972` + + +## Downloads + +Downloads can be obtained in the [Actions](https://github.com/Dreeam-qwq/Leaf/actions) or [Releases](https://github.com/Dreeam-qwq/Leaf/releases) + + +## Building + +Building a Paperclip JAR for distribution: + +```bash +./gradlew applyPatches && ./gradlew createReobfPaperclipJar +``` + + +## License +[![MIT License](https://img.shields.io/github/license/Dreeam-qwq/Leaf?style=flat-square)](LICENSE) + +All patches are licensed under the MIT license, unless otherwise noted in the patch headers. + +See [PaperMC/Paper](https://github.com/PaperMC/Paper), and [PaperMC/Paperweight](https://github.com/PaperMC/paperweight) for the license of material used by this project. + +Yes, this also includes all API provided by Paper, Spigot, and Bukkit. + + +Credits: +------------- + +- [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) +- [Gale](https://github.com/GaleMC/Gale) +- [Purpur](https://github.com/PurpurMC/Purpur) +- [KeYi](https://github.com/KeYiMC/KeYi) \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..834f05f8 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,80 @@ +import io.papermc.paperweight.util.constants.* + +plugins { + java + `maven-publish` + id("com.github.johnrengelman.shadow") version "7.1.2" apply false + id("io.papermc.paperweight.patcher") version "1.4.0" +} + +val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" + +repositories { + mavenCentral() + maven(paperMavenPublicUrl) { + content { onlyForConfigurations(configurations.paperclip.name) } + } +} + +dependencies { + remapper("net.fabricmc:tiny-remapper:0.8.6:fat") + decompiler("org.quiltmc:quiltflower:1.9.0") + paperclip("io.papermc:paperclip:3.0.3-SNAPSHOT") +} + +subprojects { + apply(plugin = "java") + apply(plugin = "maven-publish") + + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + } + + tasks.withType { + options.encoding = Charsets.UTF_8.name() + options.release.set(17) + } + tasks.withType { + options.encoding = Charsets.UTF_8.name() + } + tasks.withType { + filteringCharset = Charsets.UTF_8.name() + } + + repositories { + mavenCentral() + maven(paperMavenPublicUrl) + maven("https://oss.sonatype.org/content/groups/public/") + maven("https://ci.emc.gs/nexus/content/groups/aikar/") + maven("https://repo.aikar.co/content/groups/aikar") + maven("https://repo.md-5.net/content/repositories/releases/") + maven("https://hub.spigotmc.org/nexus/content/groups/public/") + maven("https://jitpack.io") + maven("https://repo.codemc.io/repository/maven-public/") + } +} + +paperweight { + serverProject.set(project(":leaf-server")) + + remapRepo.set("https://maven.fabricmc.net/") + decompileRepo.set("https://maven.quiltmc.org/") + + useStandardUpstream("Pufferfish") { + url.set(github("pufferfish-gg", "Pufferfish")) + ref.set(providers.gradleProperty("PufferfishCommit")) + + withStandardPatcher { + apiSourceDirPath.set("pufferfish-api") + serverSourceDirPath.set("pufferfish-server") + + apiPatchDir.set(layout.projectDirectory.dir("patches/api")) + apiOutputDir.set(layout.projectDirectory.dir("Leaf-API")) + + serverPatchDir.set(layout.projectDirectory.dir("patches/server")) + serverOutputDir.set(layout.projectDirectory.dir("Leaf-Server")) + } + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..eb10c941 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,9 @@ +group = org.dreeam.leaf +version = 1.19.3-R0.1-SNAPSHOT + +PufferfishCommit = 9771bd7e692b5f4133e5846123c2f3a9c315e75f + +org.gradle.caching = true +org.gradle.parallel = true +org.gradle.vfs.watch = false +org.gradle.jvmargs = -Xmx3G diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..943f0cbf Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..f398c33c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 00000000..65dcd68d --- /dev/null +++ b/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..93e3f59f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/patches/api/0001-Leaf-config-files.patch b/patches/api/0001-Leaf-config-files.patch new file mode 100644 index 00000000..e1c0b1d2 --- /dev/null +++ b/patches/api/0001-Leaf-config-files.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Thu, 11 Aug 2022 04:13:27 +0800 +Subject: [PATCH] Leaf config files + + +diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java +index 2204336d8800311b65e894739ab1b27273e7c6f2..39808c0cc23a97f707366378f28ba64b80e6acbb 100644 +--- a/src/main/java/org/bukkit/Server.java ++++ b/src/main/java/org/bukkit/Server.java +@@ -1971,6 +1971,14 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi + } + // Paper end + ++ // Leaf start ++ @NotNull ++ public org.bukkit.configuration.file.YamlConfiguration getLeafConfig() ++ { ++ throw new UnsupportedOperationException("Not supported yet."); ++ } ++ // Leaf end ++ + /** + * Sends the component to the player + * diff --git a/patches/api/0002-Bump-Dependencies.patch b/patches/api/0002-Bump-Dependencies.patch new file mode 100644 index 00000000..3f3e623d --- /dev/null +++ b/patches/api/0002-Bump-Dependencies.patch @@ -0,0 +1,74 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Fri, 28 Oct 2022 17:24:16 -0400 +Subject: [PATCH] Bump Dependencies + + +diff --git a/build.gradle.kts b/build.gradle.kts +index a995ecc3b1d6181c58d5b4a0a6a893178bdc40aa..63f4b9697415f073937ae08bafb6b8b0b85f174d 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -25,13 +25,13 @@ dependencies { + // api dependencies are listed transitively to API consumers + api("com.google.guava:guava:31.1-jre") + api("com.google.code.gson:gson:2.10") +- api("net.md-5:bungeecord-chat:1.16-R0.4-deprecated+build.6") // Paper ++ api("net.md-5:bungeecord-chat:1.19-R0.1-SNAPSHOT") // Paper + api("org.yaml:snakeyaml:1.33") + // Paper start + api("com.googlecode.json-simple:json-simple:1.1.1") { + isTransitive = false // includes junit + } +- api("it.unimi.dsi:fastutil:8.5.6") ++ api("it.unimi.dsi:fastutil:8.5.11") + apiAndDocs(platform("net.kyori:adventure-bom:$adventureVersion")) + apiAndDocs("net.kyori:adventure-api") + apiAndDocs("net.kyori:adventure-text-minimessage") +@@ -39,18 +39,18 @@ dependencies { + apiAndDocs("net.kyori:adventure-text-serializer-legacy") + apiAndDocs("net.kyori:adventure-text-serializer-plain") + apiAndDocs("net.kyori:adventure-text-logger-slf4j") +- api("org.apache.logging.log4j:log4j-api:2.17.1") +- api("org.slf4j:slf4j-api:1.8.0-beta4") +- api("io.sentry:sentry:5.4.0") // Pufferfish ++ api("org.apache.logging.log4j:log4j-api:2.19.0") ++ api("org.slf4j:slf4j-api:1.8.0-beta4") // Leaf TODO - Bump later, need to fix breaking compatibility ++ api("io.sentry:sentry:6.10.0") // Pufferfish + +- implementation("org.ow2.asm:asm:9.2") +- implementation("org.ow2.asm:asm-commons:9.2") ++ implementation("org.ow2.asm:asm:9.4") ++ implementation("org.ow2.asm:asm-commons:9.4") + // Paper end + + compileOnly("org.apache.maven:maven-resolver-provider:3.8.5") +- compileOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3") +- compileOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3") +- compileOnly("com.google.code.findbugs:jsr305:1.3.9") // Paper ++ compileOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.2") ++ compileOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.2") ++ compileOnly("com.google.code.findbugs:jsr305:3.0.2") // Paper + + val annotations = "org.jetbrains:annotations:23.0.0" // Paper - we don't want Java 5 annotations... + compileOnly(annotations) +@@ -64,8 +64,8 @@ dependencies { + + testImplementation("org.apache.commons:commons-lang3:3.12.0") + testImplementation("junit:junit:4.13.2") +- testImplementation("org.hamcrest:hamcrest-library:1.3") +- testImplementation("org.ow2.asm:asm-tree:9.3") ++ testImplementation("org.hamcrest:hamcrest-library:2.2") ++ testImplementation("org.ow2.asm:asm-tree:9.4") + } + + configure { +@@ -147,6 +147,9 @@ val scanJar = tasks.register("scanJarForBadCalls", io.papermc.paperweight.tasks. + jarToScan.set(tasks.jar.flatMap { it.archiveFile }) + classpath.from(configurations.compileClasspath) + } ++repositories { ++ mavenCentral() ++} + tasks.check { + dependsOn(scanJar) + } diff --git a/patches/api/0003-Purpur-Lagging-threshold.patch b/patches/api/0003-Purpur-Lagging-threshold.patch new file mode 100644 index 00000000..a6b7dd8b --- /dev/null +++ b/patches/api/0003-Purpur-Lagging-threshold.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Tue, 23 Jul 2019 10:07:24 -0500 +Subject: [PATCH] Purpur: Lagging threshold + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java +index ac9b690fcccb60b587e5345f12f1383afd0a73a1..0a4320b32485db84a0be16535b406f043e3177a2 100644 +--- a/src/main/java/org/bukkit/Bukkit.java ++++ b/src/main/java/org/bukkit/Bukkit.java +@@ -2464,4 +2464,15 @@ public final class Bukkit { + public static Server.Spigot spigot() { + return server.spigot(); + } ++ ++ // Purpur start ++ /** ++ * 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 39808c0cc23a97f707366378f28ba64b80e6acbb..8eb8a788dc3dbc0d5ac24089a57c730089fd8dbe 100644 +--- a/src/main/java/org/bukkit/Server.java ++++ b/src/main/java/org/bukkit/Server.java +@@ -2147,4 +2147,13 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi + */ + @NotNull org.bukkit.potion.PotionBrewer getPotionBrewer(); + // Paper end ++ ++ // Purpur start ++ /** ++ * Check if server is lagging according to laggy threshold setting ++ * ++ * @return True if lagging ++ */ ++ boolean isLagging(); ++ // Purpur end + } diff --git a/patches/api/0004-Purpur-ChatColor-conveniences.patch b/patches/api/0004-Purpur-ChatColor-conveniences.patch new file mode 100644 index 00000000..313dec79 --- /dev/null +++ b/patches/api/0004-Purpur-ChatColor-conveniences.patch @@ -0,0 +1,98 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Fri, 10 Jul 2020 12:43:25 -0500 +Subject: [PATCH] Purpur: ChatColor conveniences + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/org/bukkit/ChatColor.java b/src/main/java/org/bukkit/ChatColor.java +index f6eb30f53dad684f156102cf7147b2f00c82c71e..f1239a2618b08fa92e0e20692d1c3d20d1558502 100644 +--- a/src/main/java/org/bukkit/ChatColor.java ++++ b/src/main/java/org/bukkit/ChatColor.java +@@ -3,6 +3,7 @@ package org.bukkit; + import com.google.common.base.Preconditions; + import com.google.common.collect.Maps; + import java.util.Map; ++import java.util.regex.Matcher; + import java.util.regex.Pattern; + import org.jetbrains.annotations.Contract; + import org.jetbrains.annotations.NotNull; +@@ -413,4 +414,77 @@ public enum ChatColor { + BY_CHAR.put(color.code, color); + } + } ++ ++ // Purpur start ++ /** ++ * Convert legacy string into a string ready to be parsed by MiniMessage ++ * ++ * @param str Legacy string ++ * @return MiniMessage ready string ++ */ ++ @NotNull ++ public static String toMM(@NotNull String str) { ++ StringBuilder sb = new StringBuilder(str); ++ Matcher m = STRIP_COLOR_PATTERN.matcher(sb); ++ while (m.find()) { ++ sb.replace(m.start(), m.end(), sb.substring(m.start(), m.end()).toLowerCase()); ++ } ++ return sb.toString() ++ .replace("&0", "") ++ .replace("&1", "") ++ .replace("&2", "") ++ .replace("&3", "") ++ .replace("&4", "") ++ .replace("&5", "") ++ .replace("&6", "") ++ .replace("&7", "") ++ .replace("&8", "") ++ .replace("&9", "") ++ .replace("&a", "") ++ .replace("&b", "") ++ .replace("&c", "") ++ .replace("&d", "") ++ .replace("&e", "") ++ .replace("&f", "") ++ .replace("&k", "") ++ .replace("&l", "") ++ .replace("&m", "") ++ .replace("&n", "") ++ .replace("&o", "") ++ .replace("&r", ""); ++ } ++ ++ @NotNull ++ public static net.kyori.adventure.text.Component parseMM(@NotNull String string, @Nullable Object... args) { ++ return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(String.format(string, args)); ++ } ++ ++ @Deprecated ++ public static final Pattern HEX_PATTERN = Pattern.compile("(#[A-Fa-f0-9]{6})"); ++ ++ @Deprecated ++ @Nullable ++ public static String replaceHex(@Nullable String str) { ++ if (str != null) { ++ java.util.regex.Matcher matcher = HEX_PATTERN.matcher(str); ++ while (matcher.find()) { ++ String group = matcher.group(1); ++ str = str.replace(group, net.md_5.bungee.api.ChatColor.of(group).toString()); ++ } ++ } ++ return str; ++ } ++ ++ @Deprecated ++ @Nullable ++ public static String color(@Nullable String str) { ++ return color(str, true); ++ } ++ ++ @Deprecated ++ @Nullable ++ 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 + } diff --git a/patches/api/0005-Purpur-Spigot-Improve-output-of-plugins-command.patch b/patches/api/0005-Purpur-Spigot-Improve-output-of-plugins-command.patch new file mode 100644 index 00000000..275bb6ed --- /dev/null +++ b/patches/api/0005-Purpur-Spigot-Improve-output-of-plugins-command.patch @@ -0,0 +1,118 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Parker Hawke +Date: Sat, 27 Jun 2020 18:43:37 -0400 +Subject: [PATCH] Purpur: Spigot - Improve output of plugins command + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +Co-authored-by: Oharass + +diff --git a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java +index 1aa58c59e1e8738bbdc77752885ff3b18b29de42..46525191653e4ac0e0366c0357f368c0b709f20d 100644 +--- a/src/main/java/org/bukkit/command/defaults/PluginsCommand.java ++++ b/src/main/java/org/bukkit/command/defaults/PluginsCommand.java +@@ -11,6 +11,14 @@ import org.bukkit.ChatColor; + import org.bukkit.command.CommandSender; + import org.bukkit.plugin.Plugin; + import org.jetbrains.annotations.NotNull; ++// Spigot start ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.TextComponent; ++import net.kyori.adventure.text.format.NamedTextColor; ++import net.kyori.adventure.text.event.ClickEvent; ++import org.bukkit.entity.Player; ++import org.bukkit.plugin.PluginDescriptionFile; ++// Spigot end + + public class PluginsCommand extends BukkitCommand { + public PluginsCommand(@NotNull String name) { +@@ -25,7 +33,13 @@ public class PluginsCommand extends BukkitCommand { + public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) { + if (!testPermission(sender)) return true; + +- sender.sendMessage("Plugins " + getPluginList()); ++ // Spigot start ++ if (sender instanceof Player && sender.hasPermission("bukkit.command.version")) { ++ sender.sendMessage(getPluginListSpigot()); ++ } else { ++ sender.sendMessage("Plugins " + getPluginList()); ++ } ++ // Spigot end + return true; + } + +@@ -71,4 +85,73 @@ public class PluginsCommand extends BukkitCommand { + // Paper end + } + ++ // Spigot start ++ @NotNull ++ private TextComponent getPluginListSpigot() { ++ Plugin[] plugins = Bukkit.getPluginManager().getPlugins(); ++ TextComponent.Builder builder = Component.text(); ++ builder.append(Component.text("Plugins (" + plugins.length + "): ")); ++ ++ int index = 0; ++ for (Plugin plugin : plugins) { ++ if (index++ > 0) { ++ builder.append(Component.text(", ", NamedTextColor.WHITE)); ++ } ++ ++ // Event components ++ PluginDescriptionFile description = plugin.getDescription(); ++ TextComponent.Builder hover = Component.text(); ++ hover.append(Component.text("Version: ", NamedTextColor.WHITE)).append(Component.text(description.getVersion(), NamedTextColor.GREEN)); ++ ++ if (description.getDescription() != null) { ++ hover.append(Component.newline()) ++ .append(Component.text("Description: ", NamedTextColor.WHITE)) ++ .append(Component.text(description.getDescription(), NamedTextColor.GREEN)); ++ } ++ ++ if (description.getWebsite() != null) { ++ hover.append(Component.newline()) ++ .append(Component.text("Website: ", NamedTextColor.WHITE)) ++ .append(Component.text(description.getWebsite(), NamedTextColor.GREEN)); ++ } ++ ++ if (!description.getAuthors().isEmpty()) { ++ hover.append(Component.newline()); ++ if (description.getAuthors().size() == 1) { ++ hover.append(Component.text("Author: ")); ++ } else { ++ hover.append(Component.text("Authors: ")); ++ } ++ ++ hover.append(getAuthors(description)); ++ } ++ ++ // Plugin list entry ++ builder.append(Component.text(plugin.getDescription().getName(), plugin.isEnabled() ? NamedTextColor.GREEN : NamedTextColor.RED) ++ .hoverEvent(hover.build()).clickEvent(ClickEvent.suggestCommand("/version " + description.getName()))); ++ ++ if (plugin.getDescription().getProvides().size() > 0) { ++ builder.append(Component.text(" (", NamedTextColor.WHITE)).append(Component.text(String.join(", ", plugin.getDescription().getProvides()))).append(Component.text(")")); ++ } ++ } ++ ++ return builder.build(); ++ } ++ ++ @NotNull ++ private TextComponent getAuthors(@NotNull final PluginDescriptionFile description) { ++ TextComponent.Builder builder = Component.text(); ++ List authors = description.getAuthors(); ++ ++ for (int i = 0; i < authors.size(); i++) { ++ if (i > 0) { ++ builder.append(Component.text(i < authors.size() - 1 ? ", " : " and ", NamedTextColor.WHITE)); ++ } ++ ++ builder.append(Component.text(authors.get(i), NamedTextColor.GREEN)); ++ } ++ ++ return builder.build(); ++ } ++ // Spigot end + } diff --git a/patches/api/0006-Purpur-Lobotomize-stuck-villagers.patch b/patches/api/0006-Purpur-Lobotomize-stuck-villagers.patch new file mode 100644 index 00000000..a97177d8 --- /dev/null +++ b/patches/api/0006-Purpur-Lobotomize-stuck-villagers.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Mon, 24 Jan 2022 20:42:22 -0600 +Subject: [PATCH] Purpur: Lobotomize stuck villagers + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/org/bukkit/entity/Villager.java b/src/main/java/org/bukkit/entity/Villager.java +index c61e7e41aeb3d4f5f4ac47da8890051d8e97340d..12b08318f78c8144cc809dbccf0feabdd31f0ee2 100644 +--- a/src/main/java/org/bukkit/entity/Villager.java ++++ b/src/main/java/org/bukkit/entity/Villager.java +@@ -328,4 +328,14 @@ public interface Villager extends AbstractVillager { + */ + public void clearReputations(); + // Paper end ++ ++ // Purpur start ++ ++ /** ++ * Check if villager is currently lobotomized ++ * ++ * @return True if lobotomized ++ */ ++ boolean isLobotomized(); ++ // Purpur end + } diff --git a/patches/api/0007-KeYi-Optimize-Spigot-event-bus.patch b/patches/api/0007-KeYi-Optimize-Spigot-event-bus.patch new file mode 100644 index 00000000..8f0d8da0 --- /dev/null +++ b/patches/api/0007-KeYi-Optimize-Spigot-event-bus.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Bjarne Koll +Date: Thu, 9 Dec 2021 01:53:30 +0100 +Subject: [PATCH] KeYi: Optimize Spigot event bus + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +Original code by lynxplay, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/lynxplay/ktp + +diff --git a/src/main/java/org/bukkit/plugin/RegisteredListener.java b/src/main/java/org/bukkit/plugin/RegisteredListener.java +index 3b3d9642a8d63798dc28f2f8df77f0466451cbff..8d3605f25e97a375971705c737bc7bacbac045cd 100644 +--- a/src/main/java/org/bukkit/plugin/RegisteredListener.java ++++ b/src/main/java/org/bukkit/plugin/RegisteredListener.java +@@ -62,8 +62,10 @@ public class RegisteredListener { + * @throws EventException If an event handler throws an exception. + */ + public void callEvent(@NotNull final Event event) throws EventException { +- if (event instanceof Cancellable) { +- if (((Cancellable) event).isCancelled() && isIgnoringCancelled()) { ++ // KTP start - optimize spigot event bus ++ if (isIgnoringCancelled()) { ++ if (event instanceof Cancellable cancellable && cancellable.isCancelled()) { ++ // KTP end - optimize spigot event bus + return; + } + } +diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java +index c077e7c883613fcb6e559b4e4776e794caa3b363..ba869354adc59db2fc547c481c1ed4d5d0af23b7 100644 +--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java ++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java +@@ -656,11 +656,15 @@ public final class SimplePluginManager implements PluginManager { + @Override + public void callEvent(@NotNull Event event) { + // Paper - replace callEvent by merging to below method +- if (event.isAsynchronous() && server.isPrimaryThread()) { ++ // KTP start - optimize spigot event bus ++ final boolean isAsync = event.isAsynchronous(); ++ final boolean isPrimary = server.isPrimaryThread(); // Cache to prevent multiple thread object comparisons. ++ if (isAsync && isPrimary) { + throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); +- } else if (!event.isAsynchronous() && !server.isPrimaryThread() && !server.isStopping() ) { ++ } else if (!isAsync && !isPrimary && !server.isStopping() ) { + throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously."); + } ++ // KTP end - optimize spigot event bus + + HandlerList handlers = event.getHandlers(); + RegisteredListener[] listeners = handlers.getRegisteredListeners(); diff --git a/patches/api/0008-KeYi-Add-Java19-Support-for-SIMD.patch b/patches/api/0008-KeYi-Add-Java19-Support-for-SIMD.patch new file mode 100644 index 00000000..4f178a67 --- /dev/null +++ b/patches/api/0008-KeYi-Add-Java19-Support-for-SIMD.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Thu, 27 Oct 2022 18:47:33 -0400 +Subject: [PATCH] KeYi: Add Java19 Support for SIMD + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java +index ab5fea0b03224bf249352ce340e94704ff713345..42288a065acee7e3181364bfadb26d3c1dc6fdc2 100644 +--- a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java ++++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java +@@ -15,7 +15,7 @@ public class SIMDChecker { + @Deprecated + public static boolean canEnable(Logger logger) { + try { +- if (SIMDDetection.getJavaVersion() != 17 && SIMDDetection.getJavaVersion() != 18 && SIMDDetection.getJavaVersion() != 19) { ++ if (SIMDDetection.getJavaVersion() < 17) { // KeYi - Add Java19 Support for SIMD + return false; + } else { + SIMDDetection.testRun = true; +@@ -36,5 +36,4 @@ public class SIMDChecker { + } catch (NoClassDefFoundError | Exception ignored) {} // Basically, we don't do anything. This lets us detect if it's not functional and disable it. + return false; + } +- + } diff --git a/patches/api/0009-KeYi-Player-Skull-API.patch b/patches/api/0009-KeYi-Player-Skull-API.patch new file mode 100644 index 00000000..d33aaa03 --- /dev/null +++ b/patches/api/0009-KeYi-Player-Skull-API.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: nostalgic853 +Date: Sun, 20 Nov 2022 00:20:01 +0800 +Subject: [PATCH] KeYi: Player Skull API + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java +index 9f762cf670bf5db9138e468e72e57781d8d22f54..f8bcea8fa62a63d61fb16a51cf7c05cbe09fc825 100644 +--- a/src/main/java/org/bukkit/entity/Player.java ++++ b/src/main/java/org/bukkit/entity/Player.java +@@ -3,6 +3,9 @@ package org.bukkit.entity; + import java.net.InetSocketAddress; + import java.util.Collection; + import java.util.UUID; ++import java.util.concurrent.CompletableFuture; ++import java.util.concurrent.Future; ++ + import com.destroystokyo.paper.ClientOption; // Paper + import com.destroystokyo.paper.Title; // Paper + import net.kyori.adventure.text.Component; +@@ -2903,4 +2906,23 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM + @Override + Spigot spigot(); + // Spigot end ++ ++ // KeYi start ++ ++ /** ++ * Get a skull item of a player. ++ * This method runs on main thread, which may freeze the server. ++ * ++ * @return A ItemStack of the skull of the player ++ */ ++ ItemStack getSkull(); ++ ++ /** ++ * Get a skull item of a player. ++ * This method runs on main thread, which may freeze the server. ++ * ++ * @return A CompletableFuture the of ItemStack of the skull of the player ++ */ ++ CompletableFuture getSkullAsynchronously(); ++ // KeYi end + } diff --git a/patches/server/0001-Rebrand.patch b/patches/server/0001-Rebrand.patch new file mode 100644 index 00000000..8316e8d4 --- /dev/null +++ b/patches/server/0001-Rebrand.patch @@ -0,0 +1,683 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Encode42 +Date: Thu, 16 Sep 2021 20:39:45 -0400 +Subject: [PATCH] Rebrand + + +diff --git a/build.gradle.kts b/build.gradle.kts +index 04a1e3c9619b41f429bd598d55c9e0b5abaff920..c8f031712006d95c59027791be1bdcdc2bd836c4 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -7,7 +7,7 @@ plugins { + } + + dependencies { +- implementation(project(":pufferfish-api")) // Pufferfish // Paper ++ implementation(project(":leaf-api")) // Paper // Pufferfish // Leaf + // Pufferfish start + implementation("io.papermc.paper:paper-mojangapi:1.19.2-R0.1-SNAPSHOT") { + exclude("io.papermc.paper", "paper-api") +@@ -81,7 +81,7 @@ tasks.jar { + attributes( + "Main-Class" to "org.bukkit.craftbukkit.Main", + "Implementation-Title" to "CraftBukkit", +- "Implementation-Version" to "git-Pufferfish-$implementationVersion", // Pufferfish ++ "Implementation-Version" to "git-Leaf-$implementationVersion", // Pufferfish // Leaf + "Implementation-Vendor" to date, // Paper + "Specification-Title" to "Bukkit", + "Specification-Version" to project.version, +diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java +index c5d5648f4ca603ef2b1df723b58f9caf4dd3c722..bf670b956b20d670f84f1ef76badaae7f0c20e5b 100644 +--- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java ++++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java +@@ -17,7 +17,7 @@ public final class PaperConsole extends SimpleTerminalConsole { + @Override + protected LineReader buildReader(LineReaderBuilder builder) { + builder +- .appName("Paper") ++ .appName("Leaf") // Leaf + .variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history")) + .completer(new ConsoleCommandCompleter(this.server)) + .option(LineReader.Option.COMPLETE_IN_WORD, true); +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 68d16efaf9c2d997afabadcf1ee24c5de685b5b3..c69cb21f2ccf0f93a7ec17d4ee1ddb0ba5cfa5b2 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -924,7 +924,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! ++ return "Leaf"; // Leaf - Leaf > // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! + } + + public SystemReport fillSystemReport(SystemReport details) { +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 277e8a03ab270eabb6b8c31d0076f3310c91eef2..07b40f503358987339937bb8db5a3ee54c1b08c8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -261,7 +261,7 @@ import javax.annotation.Nullable; // Paper + import javax.annotation.Nonnull; // Paper + + public final class CraftServer implements Server { +- private final String serverName = "Pufferfish"; // Paper // Pufferfish ++ private final String serverName = "Leaf"; // Paper // Pufferfish // Leaf + private final String serverVersion; + private final String bukkitVersion = Versioning.getBukkitVersion(); + private final Logger logger = Logger.getLogger("Minecraft"); +diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +index cdefb2025eedea7e204d70d568adaf1c1ec4c03c..38920d680d9b0d81013dcf16ce3dd7271eeafe4c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java ++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +@@ -504,7 +504,7 @@ public class CraftScheduler implements BukkitScheduler { + this.parsePending(); + } else { + //this.debugTail = this.debugTail.setNext(new CraftAsyncDebugger(currentTick + CraftScheduler.RECENT_TICKS, task.getOwner(), task.getTaskClass())); // Paper +- task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Paper"); // Paper ++ task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Leaf"); // Paper // Leaf + // 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/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java +index 80553face9c70c2a3d897681e7761df85b22d464..3e14c02c2ed3d687c866087bf7477918f45e3719 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java +@@ -11,7 +11,7 @@ public final class Versioning { + public static String getBukkitVersion() { + String result = "Unknown-Version"; + +- InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/gg.pufferfish.pufferfish/pufferfish-api/pom.properties"); // Pufferfish ++ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/org.dreeam.leaf/leaf-api/pom.properties"); // Pufferfish // Leaf + 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 e9fa7faaa4451e36b3908cbcbbe0baf213abde96..6466e8bd54b38e4f432101c6d1a397db1066d341 100644 +--- a/src/main/java/org/spigotmc/WatchdogThread.java ++++ b/src/main/java/org/spigotmc/WatchdogThread.java +@@ -155,14 +155,14 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa + if (isLongTimeout) { + // Paper end + log.log( Level.SEVERE, "------------------------------" ); +- log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug." ); // Paper ++ log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Leaf bug." ); // Paper // Leaf + 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 Leaf bug, please report this to https://github.com/Winds-Studio/Leaf/issues" ); // Leaf + 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, "Leaf version: " + Bukkit.getServer().getVersion() ); // Leaf + // + if ( net.minecraft.world.level.Level.lastPhysicsProblem != null ) + { +@@ -185,12 +185,12 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa + // Paper end + } else + { +- log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); ++ log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO LEAF - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); // Leaf + 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 Leaf!):" ); // Paper // Leaf + io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper // Paper - rewrite chunk system + this.dumpTickingInfo(); // Paper - log detailed tick information + WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); +@@ -206,7 +206,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa + WatchdogThread.dumpThread( thread, log ); + } + } else { +- log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); ++ log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO LEAF - THIS IS NOT A BUG OR A CRASH ---"); // Leaf + } + + log.log( Level.SEVERE, "------------------------------" ); +diff --git a/src/main/resources/logo.png b/src/main/resources/logo.png +index a7d785f60c884ee4ee487cc364402d66c3dc2ecc..9597d58066bdbca302d790e6c195204b7d878011 100644 +GIT binary patch +literal 13210 +zcmc&)1ydY6w_ae;A`29^#oe{ITXBjPcXzkNDekVtin|vp?oNTl-QBJC{eH#G%*mO^ +z#Lgr+^88Ryl0icuL;(N*XmYZWYX9+({}B@0e|c88LDhc@Xep*B1_0E@e|j^92LMpy +ztR*B=T-9X6!E#a(JnUb1c$hg@*Z~09AGzwD+R2Z&!kdrg7@FeCSM}+3K>!NMDx?vR +z&QDMhz&I3yB{~=uiK6)#2Qx=jV<31DMK(8br+}`_QY-<@x#R +z-`C%VIfrjk-qRQmOe~tjgP%5lFKS_e{L5tV%CZBaT@Y9t*)1R#bTIzM5`GW>xPtTc +z3UI3vd4s8S1i%8E7FnpUhPVHPbg0m10l^kw%aj6KwAvKtYu# +z1q-mZ1_q+j!XX0->4FD%#AzpBh#CPPnc_?n05BgcK!Ox|5)P~YSHL#cvsG^B6xJpS +z1Lz%3olO5a$N9M}b%xOM=4xw)eCLl{y|^(RJ;(xik0K{Mn-W|05KdtV6#yW7p6&bK +z6G=Ec*xNp^IJCRG8~piUd*w(WOn$xj@jC_<2mmQfU#=SN?=QhQ4a3yA_t=)}0ai5u +zR-4cIoKWNfU6dK0=V))fH&N2OLX{Cg5@M83pU$aG3cJ{LqlN!*ZaZ#82R!`O`T_O1 +z0yt-hW>)tWg}gf(J9yNn#T||V#n~Mm4=F!}!vf#q=x2Ah>c2DLL|tqDXj7q39QJNk +zOOzxZP2Isd4&=BdOM6F7_Cy0zg)8?ewIDW++w}%qz`#bMYtMNAL<4S_V+cPb0zj5r +z9Nv!rKyuG>yeK0KU^2aU7XbKug#Z0l3ZB9c5&$4s5X4X?hKSntxvCdEr;p@!FCMHh +zf}|)aRj(jO9AOVaj8cf7CPwhBmwy@9Tn9&3fSI)oYU*R>Lp$8U`W|%Zgoj{+pZ5p< +zcQ`UoYy=70n4CWn(*)R(2aVRrfa +zCHA}MO9FHR%n|h#^;?ELC8$9aZUNR^wEY(uVboB8>#Q3WhCU?ir|m3Z6R8lSNCnFtPUhC~PpYQ-s7^4#Vk`@oWX4)M%_3f0N-yal9+NdA0XIr{<+ +z6;}0#a%?X5Eb7T@T#4~w=Ay-!ENe^aM=RE2w+`g>5K^i1ne;i^BgEsw~5(a&!e(UPXN{;*#`4rleFEbpo2y^|CO|?aJKsE2G&uaIo$+^op?)mBJ +zrMpwEXf9LkJ-j@&Y*lWPxNrwYMS3Te41j~V^xNxu%@@>z2@I4xux%?OsDosT6QW#TG^=C{Mq9z +zzpAsV2RyRvH$U%uj;EiazgKryWj}j6LpjSjLpcBPVEjP+VE-^pGC_<*vLxUp;OFAz +zKH*{K^!v1Uo2$QkpuGQB%aX{l&^7m#8?W?h!Z=pCM>?LmeOXkQL%z>=>OSE`$6WLL +zeNlF2_FDCd{lfZ$(4c?it0GG_(WrjE(52Yj)uZ-~^Tcj$S$5f<+k`__0}lfogV!!n +z<;EPEyd^gK5$J&4R@b<%5x=mI`ODNXobZq^Y`#x^M0aU7#imd`@cO73I(=GtDX>swetg^9Fvok%*ZiBQ{v;}!Z +zJV{-rW4U2dA)o))8(1C?`myT@duW%%nX{MmEzQleW`FHxJKn+Z)Oen!pbocAf*)U4 +z23C55$bJY|T!j^1dB4m-kp%lDG&@XLs{3c`Pu2R?HhICMgIvwj2flRye~A~XaAj%t +zAx>mvHAe45f~0oJTSZODrH!iT73US^FDW@GIYnw{)@e|159``7pU2mZ{c3}#$MNMioD(JWhtiKq&^zik(+GM+Abr(1{Ej~`3DL)0{`gJAL +zskGHKF1LB8X|PvJqRH3BN+CCc2K@Co;5`-*7ARvAK_%Y?>UKgyS~m +zKG&OQse;}V@)SMz&h-7NL<-Bmd%oa$fepWMt0Y`sT1mGx$v2CcOaLK-F>ORaJua#GjODA;wyce9jTV__4m#ZWqw2-;x{iyJkX{)cQ-(@K= +zohO3lcA?N}_&9pIaTu?E_1o!=;?V?3-TJj!s76R8!0CNkwpP#a)ckS$TuZRV?N0Gj +zVD@r8@WrNg(e>}sUl#p*@2<W=#-kB4(SPm~LVHGQ9WuiCfn)%O#` +z7mSm@rjL5_Ui+;lqh6!wejSWUbmWMd-A +zWesK6(_;ae|GZCKD-So{cV53vYridDeR__b?y2!Ezje8+EU)DsrW_{wJ>@NQUHVo% +z1@*sdyX@E;?jp;idlUUoxt)D69WI+ueiT0R`*>r048472mf@TJPss$E%Bm>>06sJT +z03;Luc=@jias&XlvjG4nMgRbR1^|HPl+&Rq2mrvut0;Vv`me*kfB!x{KHlHo-`?I{ +zUtgb}pC29`?(gq!Zf>rwt}ZVx&(6+HPfw4Ijt&nG_xAR7c6PS5wl+66*VfjcQ0U6a +z%F@!(;^N}`{QS(!%=Gm1( +zTUuJ0o0}UN8tUrmYHMq&s;Vk0E6dBv%gV}%i;I8${8>;?ke{EQo12@Jm6e&9nVz1W +zl9G~~oSc}L7#9~86B82^6%`p784(c?8X6iL91MX#0s;d3{r!D?eZ9TCJv}|$-Q8VX +zU7eks9UUF*?Cfl8Y^<%VEi5d|%*;$oOpJ_-3=9nP_4Rdib+xs%H8nLgG&I!I)KpYd +zl$Dhg6cps;<>ln$q@|@LB_+kh#l^(LgoT9#1qJ!}`T6+xczJobxVShtIM~?OSXo(_ +zn3xzD8R_WgXlZGwsi`R`DZyYc85tP~2?-Gq5di@KE-vop&!4fdurM(((b3V-(9lp& +zP>_+45fBi7Kp-qEEDQ|He}b?I*$DVgBw=o9O6va=|KAh9-pWAyzXc&X$?CWQ0O$k% +zBbZc1bRqzNCSFcb?3-84xxbDp)o9|zA!ee^Bh0sLt2Ik936_Xrk7kRxkdGJznqzah +ziwmc4PEn1*@bP{RMH3}a=(1D +z&|*aJ%U%R_aG3DF7wg#&Bbu!|ou1+!pgaS@Ns3((VSm*HaZpg^QQjodC+i{Mj}OP& +ztW(24-;seiIG6#yS_?^7;yit%kn;MEDkH!Hz=G$|o`f7-`0YBzgo`)ObD3Q$ZoP~k +zs7{(p@o=KT^XlxExsy5i#c<41UOUKC#P0_Bo87kuYG<8rObT$yw+IDNe8LFK*4uus +zZ6zXYgc%s*TKfc+1jr)dkqtu5uE`9zGLRrtuJ<_@xsj+r)hj*UQ(|swyXobaGy9TQ +zA!``eVdSyIz8ocx)#!V|NG`z9*RuacKxNy(NT*N1JpLTJ>Tz117n7Sd@M2K&N*bl<@&~`3=F(TV(kyaz{i;Zw|xvrILWbgLG_L +zQjYTwQ>{D2P8c#l5SxtU;cx?~rfXPH5+*PDQwY2`EEuF``U#_Zf`6nlEcjEmDz2~{ +zcz~gq4{w}DsESKTejt2}2;%ok%#H$1Oj?Reh_FAy-r0%b`}E(GtxF~ +zv|cP%b87Zbc~JQGSpb58hXhj`D?6opux>N1Sy?vhUh7-H>i>f}shs&QjtF0MC>&!8?nDlWr& +zSsC+$?$mRqoD-8RG&?eFh*VQk%)4GsSo)?)npL-WUL>@+mpmB-IT=~dD>C{HZ<8PA +zT(k~MTHr!3@FTeedEXgx9DOOt9Y}BmUZnl^=Oa);u^QE9?W{X<2uHt^MUcQGwHjO% +z!qyKR%x6CIm--w;HNm)6XRpe60!>b7QP&iJX$IeIqT+6wKbXLRk>@)YAGq80Y{5{--z +zdUTSsK26d8)bI{R{m-gW^jkyhhnd<%QQviY0uJVR_mXv%{J +zWJ-IQ4~A!p=iApvClQuYwlJq37y;9w0JfAC%Gtti90aIQT6rK(Wh02##KgoxP2wPXL?bH+CxGu(bqnr5#2SFV_n?~BG!e;}4; +zAO#jU0rcnJ~; +zK3vX>Q06d9t!w*+JL1>lL1biT3I{&wGL$gJC7Gzb4NrlsY%Ks@S#FN-LxXeSe0RYy<5L^ph#j70FpNfGWMq +z292pJSU;S0(AlUj;Un2le{CJj{c$q7Z@hia?N1vAo0xRhb-#K@BlA#!ktQC_hClVZ +z{d7{=&Y)htmZvqj5euzk?dHFCz!3b+3RcMq!^zk8OBk^@6%as_noefJ$~wjZoU~xF +z_o@h4hw6CpJoJZ;`Ql7V%7*QP^*31;IyLIng;k_ab_1`Wta<+~DKEoQ@4Eox2p~CD +z*0K`yjn!5AMo>5!xB~*@6fa7~%=#3 +zU@OB91zyo5zT2H*u%hsagt5*yo2rqwJXz5lUI>cNA{*;ZrgO$Xtt_PYsE*??(M=}7 +z!Kjn|zXem?%?_clKif+dx>oN%Q1dEv1sx@Uu`-G5J$7G()L>^oP}zziqVvcVw>iixpF=Ps{6B +zuQ5j}{LBk6xiS90($ya|`Bu**41tAe^=_0<)MZHlzI6M0ug- +z4efyaE$C!Nkh3Gg9NKD+W#E8lW6ZR0CKhAarf@Cib*y`SD;D&4Kf?Ou?dUYN;w#`f +zcEvIYw#uX-n*kXxJHYU>(528UR29lvEFggo%R_9Oy1{N +z)9Q#23hWK4muc)VVEnzt@CER8E*J{I0taa+X4P_{JYVx+0Y|ph=0>u7uk0Fk%4`X0 +zYr%j>IVZ?S)9=SDGGhc-Qw+;L?PEjIEA2iI7+mpagN&D~9a?MDkjTjI1UZ3R_vQyy +zrky+6ajkL;M##Heo=ZuiVPTmw7MY{T*;A;uJuYrm5D~;t6C_=XIr4>; +zmu;`5dT`;u@`46+%X}S+6?kLKO2d8kD2zN{>oUB9p}^*c!Rw`$%zi4=5a!o~DA +zL2;T(zT`Gpx3dWCb4Vt@nvPG9lQ$8fz6=$v@L?L+a*uu-QztN^$I2V`>s;jH1Hpwn +z?YPl-zTJ$RO69&u0C`FqHtLSw@iWh48@qJjUga#zr +ze`DB}Z=Z8(8jtBcnwans%w~41y5woXEWT_(#s`0`u^>6Dxr1)$e#Pf*=1QBaa!kM9 +ze^Po33_oqrmephsOpz_a+Rlj_7+$TmY5qq4ca?%mSb+16T3BRx|ny +zshR_a*%;?s-D5hg6nv$RVHFThATs#UJ5E8-#CLrVhjugF7h%84KuYYgfQ`Xs|Er=_ +z7W7jfVm7A5%#0gDozK%wn?GjJ1>)hN$eJ_Jz9<_PKRf9s^F&?(6*iLnghLX6cSX^n +z+D;y6m0sL4eoum)5P&fnK0#hqY{t!jShD1!bL$TV_DHnOYCsH%HiaW9hi(v*J-vk2)bk@|=F2Rlp8)^hr=RBS$lhNa5Ch*kKcE2)Thbe&>$hGN5Yr!?MzQ7j| +zfwYnB`S+qCaXsBWBO42j+gtfYGRs8kmSy{vnkAgNx~kNwQ!Ina!9aDeLcz?!caBZI +zHES_hJWng}{9=We$w?|W#(ccIb7Bo*8nG4u^4r73xU-N@1#6imbX~2XaZpR}MVlAK +zX(XG<=<)2@$+JObDhi6zu&Jq^KtioFmj9~?+xXVZjsic+M43SL@L(yT$>dxVgMKZa +z0oiS+YGA+~PS@#WzvfKbqtDSrY!Ilx-Gg +zR-$6>Y)w+ZOjtl +zFXi3spSe!tazAZarF+?8Rq9Ez^f)JJ=KNl4Y_zd +zVDfNh`B>vN0a4S|{GOw#u-CDiCW?uW8MbZU1+E +zqRWOXDNiXzX-c?Gs>Z#kS<8W6NKx@~40M5|^;Kk;6|1d98d<=q&)&N&x)s5QIcXPr +zc0#$eRc|lqMC^z*cKWkyXh^@QQsv?=t%^zMvRy(e1S2ErbUc}#Hq^cioq;N|sF4>n +zzdxgYov-P+0Ky$v_u2YrYPV<$oUnCu-jCDp2<1GR+!p*CRXLR+Cq;%2p8aM2=`>w? +zK3A#Jpi+}4RcUw@j8c-GE=w7z$03djFYmBzKrJMYt-%BW6d~B+UFK+mbrHUFgu|DZ5@^KC^uh;}?6BLz> +zKIGbq11nZKxCQHrQ7Zp_I_p?riOg}YjnaZ)fcg=nt*ii_iAewxqIdTDVU?Bg80JWq +zis3WF`pchWPp&^}2$D(0nm<1rp${96w#J +ztP~I0n%NA8O*MN?P<+;S?hhZXE#K4|0;!1(U(WfT63ha@eDz$vUyfe_`junc9W5fa +z_^4#HgGBjA +z=fTq}`c(9AGM-NDU(FGMZ^pI71=g5}8!|zLX33X-_w7lI%7}YiHKI?5F$++w-pq15 +zQ)R8ejL~T~sWyEXZexwVPcu!5!OzQ&u1b5v(2e<8sEAdCM$}U+eyntFcQ_a=K}hz@ +z0bPD7Q%_`%v&7d5TMgZTajQ7*40l6D{-G_IX))hv-TKrA!N<4`YnME5Z1JoN$hBmp +z83~6WHv;f5$pRR-g+v-2Rsx?M^3j`uFWfR}1}nb_mb=`qo+3JkVarQtb;+OCIE-Zn +zJ$2KDmElP@W+qpwSHr?Y1E0NvSd>h`#h)jtFmvCIwndJn<9eP?K&q*pgf*Zu{cfTv +z?QbW#8k?l&U0gsc3VvdUy7fA4ef +z9B@5o?zgVpsIxy|0KRS8K~UH|~Gu +zxWR|j9m0a)8Yh99sm=_&poSFAc)8f$TbH~3UEzhD1&UHxT3~19pWERGnU~Q@Ts$2e +z7!*+!dm48og=(to#vca8_N@5T{{?*G0 +zlgGf#*Y80{eG1zvE8AyftcJwCe{OA3XVNoOOsr0;=#P^AjuIL;osF8dE~0*v(9^WT|Kv5d}(j_q<_zHw^6ln;@QmgIwixAJ^`6I$@NyZN|3A!hKW|{wG=t +zoD*dZ4f~rj)V&ZWjjwXOUv9WR#nZByHoTYX8@AFOBfnyN*?TUPPXK_I5%$5Cu)RIq +zO#_0fB6AtJNW%N1&avxJhT%HYt#3r%zZl7yV#J0oMTLz)VKBQtg^sM**;RLUsozt< +z>EaQU%*}-uZab1c3S@<{L+&#AvB$qUzt~+K=gEY_M1>-qcLXi8C18f9M#3#-k~H +zAUQ%}Won8HU0-BZNlfpLwV|sbvgUu9xXmPI2>EQ5)Q%V*l%KI3&fHEa-%##X@6pXf +zy6s?f$itx80{!nNkJrPeh>~G!a)1$x~g;B!_! +znYgLt?%Ir0xI)N@K;Uu>xSb8#f`>To?E}LyP%3t^wk2ChIk`L`7<%ztsjA+{lp2dc +zul7ic1oNPG0B-zS+&Jl;UL7s~4l~aHfBenF^L>p|MISq@kF>D;(QQ-zN8B(BPX7B- +z(Z8hEoxNwRY&47Gbc}=sjSXAh^htV3q#!T1X8p>?=!ttolMgvDca$&z@0)0}Yd((7meDa~unt +z?6;%gTjPb+kL%O;jbP*UqT+ +zwL^77jqppC&&-_8t$I#2RB4@@SDKCGf8Cvh82ZpchjY8(ze#h_3I4lQV>rzfezj>` +zQP&EN1Xx3L<{bNq`~<7wJ}07H +zSR9#yKjnW0c|oLtA49dBMP~EJD +zXKnKj@EG^_S5&;$^FWYNqJKQ_x_`xRKS~d(_`P!)Ec(XRR$qF!llii3jCd!{q!ZF? +z+!8_d{xp2sgVT@;059a6ah0}CKP6>Sx8L<$Ag +zTPzo9+t>(_Bve)oL#L+%%@O1}4B%j~hP2Bg8fTkt*CifZzTgKDBrfdTg-EhI9;-5% +zH!dX!eV=qmAQy!Pwh77BZH<-KX#*+C6KA&zoe(NTydPY8_7d1#t~y?|L{S^%S*9>D +zUmi4f<&}OE{<*F%|8bZ8Y?yPFud51sU%hD>Tu1G;S$uA97K$TVDkB|%Z9C#;0OeZ~ +zvGoDo(kLbggJ7}MyNXFtKVOM90amFI~|vbs7PP? +z+CrR`_O);#spc?-b_==9^akfc`sLSQdP9RI2EU5RHjo2p^J6L&cgs5S5b*>1S=i2w +zmeSqAQ$I6VThfB?S$KQ)II +zg5rtw_UQo8-hqO>owrTP(`+Is41{2KPVGU9gVJzwi)OD5e{CBVUIE__R6-aJbP#jy +zk=_n30>~_56kRG}SnY=wTg02q?$+K>)7lMSg<0M|ngE8g>T2yVe2E|Nw2UNh8mk|q +z=@Q_4^4IpjvGw6$il0TjHGhXjp8|EQffCw^B9J%>`8Mn3C80Ii1NF?z^iyXu>jwS^pEci}%=dp#5gh@zRV+-3MwKm(}C^<#w_u@sZpOPC(U-~ejX_x +z`>7YX+E(;Gz{H(GM)WDv9R?w&n20!22S2vTFW|wYapndx?ry>BhTC%@eTJ=ICW?dx +zXC9$ev|HrN7_oS;z2aRybL7>f;&O@L*sq5I(k&?qZxGpB=X-p^RZgQ+zf~V47KZ9= +zGI%4n$yiTS4@klyT#0(nr4bT+9SA_K`qrwX{HfWab6Q4qBmA@I=RDc+#jf>W2?StT +z(DwfGH=tok_xgGz{%wuGceVlol|CyMGpG)BnOHF!imYzX^Pr(=K6`RvZi&H5T;GjcsVJD&-Hcv58*l$U{@K|R +zS-Z4(Os)d?5m#1q*5|xP$IOKofooAFzLMJiUR_i{izI3EMFrf>ii$-FAN=E+i(n#> +zMdNR^bzvd?T2JDCrxdj}F(D_EiXhFKFkhn;$Q@PCUg3_>0G +zY13U?VYjUqggCH1tunT!T9iBa=-OG?1Fa|BM)AE52@;DaaT#cFWW!P8gbaws4x*-8 +zFc@nLf4|8S!F0QtC_MHf`jf6#r>c`OPJ)=s=`slkh@!Mjs9&RZR-Wa`SLX=VW0{}q +zX*y)lF(Va3vLZ((wwDgPD|>vYdO<*}cL{-yL%L-)REcsU!Aw$;D1?%k +zscDa?>1lcUX>&p<`1=FVpQA@7%1WO|>vOep@;|tRh5WnS4+PH +zFu?ype%rHNTol0HHYG0Kztny~M>tjUA_5miPMm9f4;K>t=R}oJ-Ltk_;rr(fjTU+R +zmu(3w8!rtvn&=#gv|5owxr(-V{Za*vva&pP+59Wgg%R>BFM>(GEiYPdk#d?^`*&kc +z|61z-G-Gz2YI&g@o8t{PSTHkVnnH=1gjlB3NA!Wi_EXi`0y^MiHS{vSHS(&c=graZ +z>@6P7*TvgdmcNWB+fq?1+|ZU12tX<S)TUalSDf=YQ~qKV_K`jnwyhu +zOQMBz9ypM&Ide}$Jd*!jiQM?wqckLvrO>zHTd(irk5S?%Xo!RzwK7>@2{sZ1TEM_r +z+H1~K{XACzG7KW9A#byMA6a^J<+ZjUz_t2?0F +z298^%FiBOujZ#KP#$dt0KPWWn$9GOtby +zS{~hM59$Hf4l2y)QHJl_`P-M;W_o>+E_6blKkX;D3f%AL6smaYKcb=FZ{kG0?@gDP +zoyYX&-d3-pyd`C3yA@xVBBs+u7$Rr<2(plAlp)6X;n4cA+4Tss1%Jcn +zZsntDp0{55S|VSv{qzp-$`8ZcVz{&Bx#u!6m|QdpaWwIMh61w|rZY!eU%uitpZ~{Tstyfz{LJ4l^(U +zUAnKbD436p|K5nM{ERrk{WNU8%Vd!A?2=2q`vt3;lhN1W2Q^&co(e-|48u=_?sX;{ +zE +zz48#(Ii>N%SNbF=G8H7NQ0rD}(xV1V8ZW$fts^d@B7I7&YE!6^vIxJ*+>Qv4w5~r0 +zPP0oBU0nE4z-~M75!d~9tT{gI+6Agrt;1WHz83!f`LP`TL6s*E4Td}UxrOBLL +z>6p{m^%3LnbMIc{MIgVc{HBv0NumX(2@b}bKKjj39RRc{o3G&2BboLcj?{f@=dJE^ +z^gGoHj>M7#lE1yDhvq9?#qhi+Hn#Yay`7Xj_J>!ndQn{XzvqnH@oOUkZ$#cEo~B}c +z!N}c{C~7^rNt`X{SNfDcK4Q8DwAPvQ5It10V1J-x_fXATv`8m+@*Z4xNr`IXOZ^5U#Vq~aggtL +zpj~`V8}OuQZC#*ypt@f4c68emktTjn79)H4+lyF=aT)U6QSKk=o8wLo!q*HRn`6f5rV?*HD)IX5{c +zxpy-=`|Zxo_svGBD$Agwkf4A-AaprdNp;|J21vifLD%hMrT$lZeEex|7Xe0mqFAZArC{!qp)zJmbab@utqA`6 +z61Z~|e!k$IbXNT?PvGuuzT7G514$8e!}lsR>%nURMm+~pde``@(!O=ISt0%B93;Ez +za-qRi4n0Q>zQ2#2^_y08QOl3jT*!Ir5@<8VrFx(6f9sP|H8ttjftN;wrX>jP4BcG1;MfU5x^L`zc09u!bDBt#+ll=7@ +zB;}A$BKgu}V?#qfHvm`~pt%wG2y{MOc%B!8I`p|pc +zO#?sq!Zd&j8UPmvY4RQnfo>!6{a}GFV!}g@qu<3Wu$07X(O`vikNW$~q!ngF23Ls2 +z53p8js<-B_Qd?xX6rtq43Mdz(jOg2QXx#Wng_9^1^^~KqFNq{Kvb@Ap9}bf&xFA-C +z5+#cQ`#v$A=kd0O=agATcleBaxXf_(dnqbQz|cL9R&&Ni1omTs+6~YApmk)MCghxj +z1}mq&IU>1nEiF=q=PI`%jQbyRd=hVI83Sm{E-4uTc#w;NNwEW)C(C`xvWzY_%`_MmO +zD&g-sEaE)}6(&g)y-N&rNy;5@+{M`}!{60Y8wMgF5;HmO#B~hG`W$;7xLG*yF((rq +zxP6I#r#o`B3FppK{v(q1!C+YLFSfySDcHyoW!}EfzuCB1B|C5+oP}dtocnwkcNy1EZ6#5JX4=ePl&cu~0tMnt&79+I4%PaK>VqFx;r!QdNmnxlEqdU-QR%Nmu{aWP +zJxwXvt5fFTCOVgB)Zq +z%H0U=9q7Y0lu&1kc4zYT3*lHA@XJfoK>3WFM&WWf2u6^+wCm8##D$x@Gkw+t^HoO( +z4pxDRqg;$5S=t^k22H5^V3V0Qfy%Ogl8I%LD$52=7)J>Ki9Ej1HyEi_ujELlz8$-+?cdD1Zxi02kW0 +zaY=caFq4~s^R?zxcc3Z0X|az}Aww<{P$>6rk+5Di5J7$kWor0{Q&>+DWSBH^Gf`SP +zT{4}IOFh-hB7xwBdewq%de)q6QvxorV(()2>@j8i!kj)=^hN +zl_N{$9xTHHA;V&Zx#tX&1pOO;v^NiOP#_UK@J;;lp+OOhOOO2mlMdxM;Qv-mWG+^vzox|8t`w| +z=gPlM3)y6G*hfV1WwuMe>bO-vP9g`h5BqgO9x{ROBD;aPl>XDmvt(3PUxt|4RFRpK +z5OEtRz{(Oa_W_!Z4XHf#h;Z-~71XM7wlF*L!-#h_Uy2tGuy-rAZ)4{qE~feNkp}qf +zgvBtLkFPI~I7%C=OHZfPZz$j>L9)rb;l +z@J^dxncy52;wmHg=wC3|Xn6jPYCR7xc}~D0wNjoYxmoRh_zh=6@8coM1UQIa_z*1)cZPw4v40qoZQp-uy#DLv=oP +zX9b3vzFA2r8}|_AO8W1(OMG__0{1AUD&Z%&7-(>s+Z-X6Sv}G5QguIbZ3mYa--?09 +z;wNw?n=yAag4%m#w$$-YZ{(ZJUcwHfzu&!gykNjG)e}!=q8xy2_KS=ULsQwv45NK! +zVqqD8#S{vRjg4(Q6HM_F&tihNIQns<%DVjE$cv33ET>Dvc^#{z&#u&&9RgXO?ZLuebczKv#;! +zCS|2lIa37Bp#3RWj0$V3=I2>o40{(J^LD|EUH?!2;Z&HS*>7*V%{v1)wHaUP85mcX +z%q!K}Ntr*IzJD%++btJ;VQO*OjJL1t{GvR3cy@OC-~pe^bV?N`z0QKCr?Tom)4u%A +z3mi2k&eIgh0^rGI#Di+&3lrsy-r+}zwBkDQtswtPbkj!Y^l`{f!# +zLseC0M;DiifDa!({-G4{W$Wxsgv*(NX%HMyXhArVwY105dUHg?+=@6Sy8n@slS76x +zU7%PI8ToKm#qahfR;7kn#|t@9y(0EkooWBDqA1(mpO)>BBz))giBi8xVHlj#dR9U8 +zRo%`iBdlj8%_tRn^qa%T>{nsLLwTNld&WHLyfbPzv2W62m6q=Nsdxnk +z#{P==5!Lidx3bcr_qlUl%BX!xjywA?jv>FU^mJDa0zQT9Kw8RRHq>7B +zb~DXw0(oqBrOQunsm2ghWV2i1VmN{F?)U;0%*j{FEUxazAJ3)KSWomuhklkDi?5h*MTLDS5ma_Nk1sNZYzZ#$maGRyiXBzjG@(G__fuyBl(^A>s&{jF+J%5| +zv#7nD1XK806#_U_4#N2ANAxznk%;U$Y$z#{K*O07mADqx6LjACqwP<`HFV#C6Q*wx +z8JVP_qGF}V7B?^8)f*2F5AON7v$L~Kr?2}oPai_kG!_6MI(U`LS~+Mo*CSyrw>pPE +zllqxy +z^&rnDn4XA@AUY7~`1lwTCrm8KlVRqX&!kZFH&;i9@=R}UDxNSh*)Iq2U+#9}@ag1t +z%KUOEw0DXT)>hQoLTprY^z=BC=8NAyi3pZWT7A`?;rI<3%65Nqb93%pJ=!+dNtB>W +z7f3O-e-S7ZBgBntcyt~wOG_p$AU2zlGH8=%TEm+z8kLYReEMTkIo#2YiA=iKWrH); +zS%uT3xAyyY=!U)0Evpgx{{38MPR2nN<3913M<0O#YCO=TSt^4IzV3^D%2zC>t_OO} +z_h~AVOk+IIi$Ov;-g93a4j@WaekCC#HFm2_Vu9s)8-GbYtr{LgrxnSIN^PW9)!jYX +z?%-yssA~&R3F)C)wj5i|@!atCx?Qy%P1QEGSZm;iUNai`-F(8a%y+_a>CMzx$XEKx +z>sW|JbN36s+Y{4SZsrspH%UH=+Q6J`c&_-JLGL&5|$XUA1vFOC+rgoc&xT{dFT&pMaEBKwyD;plX0>2nla;jTlQ{!fn2M=Ak*=K*g% +zBm0-$ly1~}CT-5gv){jex9)7&b8u!a+vYHXU>=NF2>g3+_rN{(LUMGwRWKk49sS$v +zazyX8zZ1hwZ|U*5{fK@i@hRl*U%Q2cg+!iIfb)6W%S5F{91qinEZE%~4Gl>rBw9S< +zMP5$exl1jESyt}d~jo?hf`z^32b!}UGtJH+w9(0UrI#~Ei*ii&6z(AVE?(}k_A +zE9Z@mj7HF-ch46I0ipe3gapRj{=zk_J1E^b_JwdrhKi4ytBuwP)m>e$@9v`A{1N{h +zwUN6H=_W+h(a?rGaQ%%LP5C4)XiZ*`1uUwgqWvk`LyDD!Ps#Q5oI($KDJ%8n5kBi- +zghsLx`~mf<>WT)6-cJBbp|htk1NfkZ@e#B4@l?UH7!MDMpO?1NETGk_Eg{z!N3!D< +zWg8gtgS%b(0Bg7dw9u35xq)1vNdnM8iu7Eje*u?#sZ~%^q*HDaZC?5z4ZzhSA%ndS +z4&$M&7(|(9nWY%QShCnuN0 +z`n9&UeypypUgx;R+x;XM#8uDM{p`9~j<49)^dotHJVO*A@HL&g7F={FP#trj@{dzm +zeQUiqRWJ&pkKkA1O-|vOf8O1UQ$$0lIExffio|}F@ROV#MXcPH$ +z?$$kxAF@B#KT}u;R@SVyIO>1sw1!i?C(_013w9@?8$bKaLQi34zC$g*^}F&(%NEO6 +zQzD-^6}HQMnGJ{h$J*)HjSxjblWegsW&rLC8Ov_r_20jLjUS$Ptnm|p9fK%r0j+4; +z57^mjL&lISh8>DC;eB$B69$h4XxE3qU4T&zUpDeV@4g>or%D-x@qhie>6mqD959ck74(h?S0BA0}YQ18d?hr6}%}y{%ZNJ^-(?=Op~; +z#2-UNh)jH9>RXmvPJ(Y!8(uhyW|sFpyvv)AaNeljHj^Fx+RC +z!`@c->W1C^FUKHmG2w_atkdsMnzY+l!CV8havQ8-Gu)<8t{#V*2Pwp4h?ayXsi5Z> +zo!guta>TA~iv#iJpQkN>#)QF%As@2WgU&V_Y^qm#E*O}M_ijJfFWq}ts)-l4>D)kCqJJ@MG2$69ph0jzwI8ry1u8D@CyinC$oT?7S*Z}Eg +zYs}PWLqr4u@)w}#!{cMx;KxO6W2H6~3k$laJjAt+C{0mmCRnfs=OJYbh}HMh&e`#> +zj;jrpjqKCh41OK{FOS`@_sPP$iCm46G^EMNk8(l-1f>!gEV+4vMVRZ#8infUenP+k +zL^tBOHF^=)k&U-Tw{gfijqQ&^ +z-RHHII5yp}2|o8pTsf6x7$teW9Em!~iy2DN?D@|U)g%I6VG%JBO$|~;c~1Q^3|x`1 +z6HRbq1#~Ke)wWpALcc&@P;m+*sGavR0{aOx3=IwUE3YPWAwV45pzD$~02inxi7(6X +z$zk683M=_r#M*+6fQ)&FK0y|lm7JLwS)K=t&ZJk!U_-y%_o@fhr{s37MUEQOF*M)3 +zB$;4>Zx;Xk*(hwFjb>1iJ1f*D#nyWL{=>{2|9*^vCNN!%bF8Oe<`xz#s;jFz?;I}4M3lL;!fy_;J-E96Of+;sG%K=fZdR)99pJ}fM( +zq%(s8UrsEL{NrdF`!#RY+VjFyPpE_vtqPMM!MQ+QnE)+_g9Z^{4^;k&Sa^=w*yuxB_*Z!U%!3{_9Qr)Jfz4IeS#io4oj_Kqhq`HCUub|Ke!v$1-$v=kc+O#rlCej?%dhY +zxxKUTsFPG1nfoFp3%7@gh9S?vM0N27#*fpJyaX;Vy{!pt*}!9_mX9uC#J5RyjknW2Dm3dCvZYU +zSW?0kvI9!o2un}*%`AYhr^CQT1aZF=-Nt^atn@Kt%b2!hT(pK!|MclbBv3-<+6{>_ +z8toMfWc9rpOk(8|KW>Z-k>Fr(xc_+q9ocf`8!_n}XYUrW?Ax|*_|=5m*4F0V+46wJ +z1IGS^Z5t=0Zj86J2MfJc +zUq#WKCfhoB<;P2&&`*_G4^_0uqDR20m!>T8ay_rxSzA&9_v5##g6tzXTkx+KRfz32 +z9vvpp?+YxHTxDthCBu7)&Q052y4s9*$M4_2w-OdPyK?F-EBoUuSsIk@@(!gA*A_!0 +z2eu1y;-Q$Ut(M>8FCOtw?vZR-%*ly^x)<95vK@P0tJoZws@+M*NGhg_NU`!}DZnWBHQz%*@6))$BWN;EM0xAF+B4Mph#S??J?K+&viwPmes*n^HGDL9iBf +zCk|mDu46wwughN!isu&G((DO>Ws`(VLY?^#w=RONxUgFGby--Y=5NJ|(>qXOS`;lZhmXyMEyBdVM@jJh71E-})~`?t4w8^Kwy) +z<+KACjs!F^TS-;FT24_iWF+=l(nR}j7U#;Vd +z)IT3=b&}A}1PUKFa6DKfgHkJci!~7u?a%k9h7Rri^{y`|;;xNDoQbV}+oJ=LdApL}|77o@C= +z;~aed)XpbrMtt1x3gHPWxbliQH4nKBCew{9 +z*-_PTyn~`1VrwKcc4ZrhI^!MsZ{D0O0%O2!SHHi^Dfyr9*x*DGFKwc()b;q6nM*M7 +zvA$x_?$BMJJHN5HIn9Ps{_7-sn79~BZegaa5V;s(BA<5BnU?^AeJHXtd)cIj_UCjA +zW|N@MjV~vrJz{sE0Dzv}tXxUDQAXm)1(kX7C_ZVFX%!TlZ850i(P1A0BxaJu)#LcH +zoxMFRzxoxw$bM=B6gpuMD#vcsa^00?%=D+T9-dQqV*=zD|)W!3BLun2&^n)~$ +z2_^{i9~sGXOAsF_S=k&4mWJ@`mD+G%MiPTlhuomboeFNwHb(< +zVpVR!mwf;JmpO3JL|B%L-!;@7TG}+`HZA;-{VIlQGY|T=f|!9!S=!c?sq5|KeEQ*~ +zm!1xeZcJPbSsfjU9e>K|=Ni<+YgrIG!|5@|Z>4bjx+`1j^O-{QK8XARf +zUG$nLRiTEtt;)9F30rvw>nj)@vCF{$d7>o2n>}~Y2^^C79l@s`uXRZOcuy>^%2@t- +zRGv={pKlDXFUgvG_^DWGR==il1rIzn{$p4r(FVOQxZi!_*Ksfl2hR{Aj>01RbFAM= +zpr0wzMwlOwlkt4|JLK)$>VL+{4nv>^`yMa)T;(9f*B(9;{T+)_=M4dN>M&&hS-#(G +z)-sW(WxVkHR)`x#g)25Lu7qnN;~Q-bvKDZ=;^fyLy@okDpvt&ZU{!U)WVtmnp +zAN-CzM{jPFWep9NAKDDq@=kynkGi_GQ@Z2y_Wn)xc_q3-&+9`qdGy_{PF-2c^$)%x +zd0sonEJhtG*2|P*Q-f_3`Akk96HzBz2 +z!5tnJaCcA2hGQrSw*{F)epvfYX?7toP=O0dN +zizY2w`>O@4Vqff!dBhQ^><#TjMP}loM9ProiD-Og@$V=*zQ|Avg0D!+96lr^u(1fl +z3J52PHoJYDdvdiIW?q?JIC*r?88VruLx#bp0lys39v$(c6uC*j}2IFFh +zViOX|K+DH18cd9%Rgjs$*sXuoW<>p^Fv-7CV|zpgTUnj812pyyX-nhA4TZ^UyYY9; +z?}BOarTT1q;0xSTjV_DPWE11?Y2+wSA*ybzebDoy8JwhznKa6SvYxE$WswX7Z6pG$ +zsA2GgHFFL3^zA@XTYK{a+6$Q8di%@1-|q9U15y+~R-L7Kwx8*xr(FP{g*JDPa`e((jSl#~?Rx=3ne(nLfeP9k0grubJK +zU4euzZqt~$Cl%k^{-!e6YQZi|D3#+MUS}VsYZ)0S>y@)kyqRI?A_esvAu-{`1Uq@! +zC+b`wnMK&<_mitl+k@e*$*{&S>vayX*>D>Q5sw2FZ?l(8ff%(8lo<^mBMrwQXOXe+ +z*7sZdWzBTIwZO$y^F)qZL1XbOMY<@M_a56y{({Vg@YN<_y}toq41V%~w=+4ZQvg)X +zVw~l$z-sId^nKU%dlk7W(mG}eS&KV2BdYqNJnX-p=YrG&&`_m0fzA_|iKD${5?oL* +zdS$heR@%Q+(3!!T&k;tIN|v2j=UI))rgkvyC7MTTrKP3g>Fma@_R0`GE5(tL%sS$7 +zG41ag%(Y(xZ5cjlk=R~(3XC+$25r*Fo=G5OhGgR}i!nDoG?^sult?Eo*x$x6CH-3L@LtZ0dfq!Bbbw-S}RwlN%lpH8c=4l2qH +z1wRszHSPh~=esnWvXD8B{D4<}?}6cA+@Ob1760Is6`g!zl@WL(L&={LA}SxAt0>Tw +z%b7i^&yNKM;(vGcNwuxAK{g|S3Y1&pH_6U1G +z3M4zx5FU=O;=l_?VzQ-~bx~xN1axPgYI0am3d25BjYmfSTX7Q}==Vcryl6@Se0(Jv +zxKW_o%H`jdnC7QXlkFbCsACHN1Dx=0gf<~@PW-&<=`1Hd)@#ypH7%OpalDj-P=ts+3^~yWs~TV}BD20HjkW6zc1L +z0#HzMkn3JV%7N-18_@tgE82*YnmEzxirriDSx#_|<|q1vL{k}7>^mRzO(ueTSN2~H +zG}kxp)Qn!&)><3|e>62+GXSpQKcemfqU!&BHZ5Ca;DT<63bBM&uV1BDS?MM$M;x8w>gShAPMxJM^BbMZn}Unm{OC9^4x3%% +zlmX8!km-u$N4fQXQ>jRe`7)3+RFGjhz +z18zf(Fo2<>YV^7LJO^UTZ2Ivd#mpN}o?7pBV&q=f%ID>haV7M8R3jsF*@a%iwIy>| +zsZ!-y{!%&j7`B?W8TcF4NH-RHH1xZ{;7BsA<#APu!;cND)te)FhoXz$BIU}2&^7WP +zT}TX>ZO58$VNPuh6JV7~s(W$vAj`^%AtUamex3YdVl3~4+pqk?G)qUibNMrj0*M25 +zY>5Ac|Dnv6xBQmV#$3JA?&HTN(lYl~J}@$l{*TY^kORrCB)3dDO}^^v!dcLf^CHty +zanjllIQeSLmpuG+h&ae`r*v!C*0A&W^a&q>93?BAXzG7n +z2*3TGPIcN`-_hY9&oaiv#fiv~>}7`T`4=pInEqWX*3e8+yPm^9h-tr&ts55$l+388 +zW)~F}2JH!}VLbQ>?6~H@&k`MnSsTeVj0TRVP4jGbP*!!CwM6`Z11c)yI2w$+R0zxo +zT|obYS1&&`{>>Z9(jnVU&=yI*%PGe*f78ie*_9oap?sd7fx7{r^WT>=XHF +zl`f{=UJEn2?tRw`Fem?eRE6#*nOes(ebRcmaK3~a3{a3EyE1zXSF0p7I_iDJ&%;3V +zU;AS}e?*mH#Yh2P9E3QBigIqu2iXf=@t)2+I~f*_E^JtEP1@IR{CBfTj%T}E3e#n% +zUa{@vU?D$l4DEANwkkK@ruP4ta)E*e^KLGg%$PizyPmHvKNMWtuJQ6sPXY=(1m#>W +z7V?9E!Vj}>a|KfQx5ESpH+q6$@gAp-P#~lbz`aj1_?xinN>3o8b2-Z3w>UZ3QZ}W0 +zWg-!>p>AADDcU^4;0*L4UFgB0QLlXd^y1E&4>txV!T|!`RwjZGl`;-4ZgFf>luHIy +zZ8d8Rh{I3r!g-ht6mAZxMB6VxRqnA0UY`h|mJZy2 +z17BazT$jMKFL3J6Ue_HL1^)4s%$Jj~Qx~1HG#tS@kwL(KP_ZI3dWz0SH(sqj#-*TNGsIWqPj>cj?!GyWvfdEiNOu4$>MIqL=F&Cc0{g*~L5 +zA1wt)=_zMFUkCT5$l!G{1-Y9QtGQ#qm5E(3fYPms_EP*sSVI)bfXN|uNO`BqVuCvd +zv)z8IGRgtM1<_trndVhQ^xA)wn~*W~#d*X@E=W)jcQWI8+?kdzHe;DZ`%+JE%gE}m +z6H=FO8rJxM{N90S=Gi!Mel)TyanxPa;E}C?hJl@e9UWad->;S|v;axgFjrY$z3(rV{MiJ}3M)t;Q?P5wZy0e3G{dcDO7n}3slDXLMrB$;#*W@Qv)D$=?Xs$F(8eTcyGIQ~IWgD%Gn&E>F9y#o>cR-7spE;Rur<_E~Pu)e0I +z#&y1|@8D~8c55<|KMf;&x;hg!A%VOZ38_+uk`jH4#=b9M&xcpxV-7cMN{jXVRnKSe +zlKJJ%=VBV{$DNeI1QkiA;DfdVT?$;O#22z6v6bTK9)fjrfIh!Hq__l~KzuNqT{&kA +zKs@YV6^1ZLGjTgR%(=NHS-DvWnnP)NM#qbHINqmQdCE5??co$3nuikqgm=s7*#Kd*+j_weKrZjMeLeHEoiJm>zuDRU` +zh~ggr^knneWU!Nn}AQt=0Id6Hk; +z4bJqse|V$H`stT?NS0yreYvaZ9YF!fw+N}{3#yXRU!C7?exl35BDC%+!jDMGT^DN# +zN9FGd#5t#;$h}5UgQ?q-Gr15>C6=nLUszle9<+_!!oi_m@_L^-R>_Qty7_g|C%m|5 +z-7^5X5V_ARi?h9_LW%2vByD3X_IvUktqBv{%SYXO1&;e&O#Ll_cfC`Wv1u+l_#RI< +zQ5Kly0;P`%TXaQN(heOg~>V&L{d+ZDA%eq-UKo#1)$rkjSm=nzAE2r +z5--RyKhxfXoGVU3^ab{5XGlyL1+26foG)4HZvN +zG@&I3h0fnK5lIjcrg*XxPy1(gK3_TN`&VYnxP;C|j$~0rT$0f|*#=OzM^NbE-1T5D +z%Csnt)n!sx3N#b(8G&+G3W~Q_B#StA6jZZ=p#wuu`DrAMXm{T@#S;ku4Dme@{Njmk +zCtrh3z6O>o)~o{&Htx+6kn*)$NNBH-biu^aYtWUq +z(G>4rCEKr#tO>!x8A@%W@6g)Xs%2Hq!y#Mbb@9R2@GDWi&!{jhZvzQ1D9nMuPoOS+ +z+cj{9nx5X{jJOIavbFf)Kz5Jnbe5Bu#(XE-z$j&iaP%c9W59OoT0~|N#D*(N2kz={ +zs(|)nH!_+_g1)#ZH2xk>ZTG#6WN#qa3BxZM{NWxq`*#$H255k6Ky?hw*hSA6`c_fl +zT@Ua%E5Ez3;~`kQFmrC#$Nlvc_Uy3#yzhd-6UYuuIwgIBZZC-`dwOBJbfurL(FfhH +z{YkjE+9OrOveY`{t{sGw&51YO1@{iO4)Ki=!Z5#q=m_Hi)_j0`>?;t2j);vv%BUif +z;wpTZdLQLsGvZ()DCdxYudn^Pt;BZ}Rin$4F8h{R`HxT2z`uc&aMXIQOvwgA5%{&) +zFW52MiN!$!EXgx}Px~e1!EMp;#&kY65oDho95j~!qD%YJr`+aK4jCJ4UJ^;q>w@Lf +zvDfg|M`S^@DGxu+7aR3Cx#;%?advj&1~L-m +zJqCP9&TW3migV*`Z$#)Qa>3>Jf)g9D6Ki28P@iX(uso)hic8Dp1F< +zeF;(n8Po8A*~^T{De(J)Z2nqLl@Vv3yoSlGwq0aeOg4ymI(KIkTeur-=J-yp9z?qe)it6gq-wl@I +z0D-_I{|T<5kwD9uH3yf1GWXp5*8eOgJf*q0IRoK|+r{}Fug&0WpNDKMTC@(Xc)9K8 +zy`lByMn!1fnY)1KYP(0Je1)c~WilUuh<&Q8^OE?L9Q^xK*Y@M$`6D6TDCZ^@l8{|} +zxmmNw)mng$hYBii+&ZqedxWT0dnV#LG4zC%+kzcK+-??vEHT>Q-T8zu|s_1IbA#OV)^+1pg1OmmZn` + diff --git a/patches/server/0002-Leaf-Config.patch b/patches/server/0002-Leaf-Config.patch new file mode 100644 index 00000000..9b7686a7 --- /dev/null +++ b/patches/server/0002-Leaf-Config.patch @@ -0,0 +1,190 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Wed, 12 Oct 2022 10:42:15 -0400 +Subject: [PATCH] Leaf Config + + +diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java +index 2cc44fbf8e5bd436b6d4e19f6c06b351e750cb31..00f30e047beefe914543718009947ecc60c21790 100644 +--- a/src/main/java/co/aikar/timings/TimingsExport.java ++++ b/src/main/java/co/aikar/timings/TimingsExport.java +@@ -242,7 +242,8 @@ public class TimingsExport extends Thread { + pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), + pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), + pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), // Pufferfish +- pair("pufferfish", mapAsJSON(gg.pufferfish.pufferfish.PufferfishConfig.getConfigCopy(), null)) // Pufferfish ++ pair("pufferfish", mapAsJSON(gg.pufferfish.pufferfish.PufferfishConfig.getConfigCopy(), null)), // Pufferfish ++ pair("leaf", mapAsJSON(org.dreeam.leaf.LeafConfig.getConfigCopy(), null)) // Leaf + )); + + new TimingsExport(listeners, parent, history).start(); +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index c69cb21f2ccf0f93a7ec17d4ee1ddb0ba5cfa5b2..3bed5eb755e97149a9651ca007564275e0eaf2d1 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1656,7 +1656,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! ++ return org.dreeam.leaf.LeafConfig.serverModName; // Leaf - Leaf > // Pufferfish - Pufferfish > // Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! + } + + public SystemReport fillSystemReport(SystemReport details) { +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index 673fb3955291407be37dc78be4eec9bf2018128b..a7023765e3c82e70574069af00227e3cf6f98e65 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -223,6 +223,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + // Paper end + gg.pufferfish.pufferfish.PufferfishConfig.load(); // Pufferfish + gg.pufferfish.pufferfish.PufferfishCommand.init(); // Pufferfish ++ org.dreeam.leaf.LeafConfig.load(); // Leaf + + this.setPvpAllowed(dedicatedserverproperties.pvp); + this.setFlightAllowed(dedicatedserverproperties.allowFlight); +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +new file mode 100644 +index 0000000000000000000000000000000000000000..770553ef8976d76067396276792ea23a7ab7d51d +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -0,0 +1,139 @@ ++package org.dreeam.leaf; ++ ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.configuration.ConfigurationSection; ++import org.bukkit.configuration.MemoryConfiguration; ++import org.jetbrains.annotations.Nullable; ++import org.simpleyaml.configuration.comments.CommentType; ++import org.simpleyaml.configuration.file.YamlFile; ++import org.simpleyaml.exceptions.InvalidConfigurationException; ++ ++import java.io.File; ++import java.io.IOException; ++import java.lang.reflect.Method; ++import java.lang.reflect.Modifier; ++import java.util.List; ++ ++public class LeafConfig { ++ ++ private static final YamlFile config = new YamlFile(); ++ private static int updates = 0; ++ ++ private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { ++ ConfigurationSection newSection = new MemoryConfiguration(); ++ for (String key : section.getKeys(false)) { ++ if (section.isConfigurationSection(key)) { ++ newSection.set(key, convertToBukkit(section.getConfigurationSection(key))); ++ } else { ++ newSection.set(key, section.get(key)); ++ } ++ } ++ return newSection; ++ } ++ ++ public static ConfigurationSection getConfigCopy() { ++ return convertToBukkit(config); ++ } ++ ++ public static int getUpdates() { ++ return updates; ++ } ++ ++ public static void load() throws IOException { ++ File configFile = new File("leaf.yml"); ++ ++ if (configFile.exists()) { ++ try { ++ config.load(configFile); ++ } catch (InvalidConfigurationException e) { ++ throw new IOException(e); ++ } ++ } ++ ++ getString("info.version", "1.0"); ++ setComment("info", ++ "Leaf Config", ++ "Github Repo: https://github.com/Dreeam-qwq/Leaf", ++ "Discord: Dreeam#0851 | QQ: 2682173972"); ++ ++ for (Method method : LeafConfig.class.getDeclaredMethods()) { ++ if (Modifier.isStatic(method.getModifiers()) && Modifier.isPrivate(method.getModifiers()) && method.getParameterCount() == 0 && ++ method.getReturnType() == Void.TYPE && !method.getName().startsWith("lambda")) { ++ method.setAccessible(true); ++ try { ++ method.invoke(null); ++ } catch (Throwable t) { ++ MinecraftServer.LOGGER.warn("Failed to load configuration option from " + method.getName(), t); ++ } ++ } ++ } ++ ++ updates++; ++ ++ config.save(configFile); ++ ++ } ++ ++ private static void setComment(String key, String... comment) { ++ if (config.contains(key)) { ++ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); ++ } ++ } ++ ++ private static void ensureDefault(String key, Object defaultValue, String... comment) { ++ if (!config.contains(key)) { ++ config.set(key, defaultValue); ++ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); ++ } ++ } ++ ++ private static boolean getBoolean(String key, boolean defaultValue, String... comment) { ++ return getBoolean(key, null, defaultValue, comment); ++ } ++ ++ private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getBoolean(key, defaultValue); ++ } ++ ++ private static int getInt(String key, int defaultValue, String... comment) { ++ return getInt(key, null, defaultValue, comment); ++ } ++ ++ private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getInt(key, defaultValue); ++ } ++ ++ private static double getDouble(String key, double defaultValue, String... comment) { ++ return getDouble(key, null, defaultValue, comment); ++ } ++ ++ private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getDouble(key, defaultValue); ++ } ++ ++ private static String getString(String key, String defaultValue, String... comment) { ++ return getOldString(key, null, defaultValue, comment); ++ } ++ ++ private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getString(key, defaultValue); ++ } ++ ++ private static List getStringList(String key, List defaultValue, String... comment) { ++ return getStringList(key, null, defaultValue, comment); ++ } ++ ++ private static List getStringList(String key, @Nullable String oldKey, List defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getStringList(key); ++ } ++ ++ public static String serverModName = "Leaf"; ++ private static void serverModName() { ++ serverModName = getString("settings.server-mod-name", serverModName); ++ } ++} diff --git a/patches/server/0003-Bump-Dependencies.patch b/patches/server/0003-Bump-Dependencies.patch new file mode 100644 index 00000000..02ba6156 --- /dev/null +++ b/patches/server/0003-Bump-Dependencies.patch @@ -0,0 +1,73 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Fri, 28 Oct 2022 17:48:45 -0400 +Subject: [PATCH] Bump Dependencies + + +diff --git a/build.gradle.kts b/build.gradle.kts +index c8f031712006d95c59027791be1bdcdc2bd836c4..99c0750dcc87f5f3b19c5e9e4e9067d49443732e 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -22,13 +22,13 @@ dependencies { + all its classes to check if they are plugins. + Scanning takes about 1-2 seconds so adding this speeds up the server start. + */ +- implementation("org.apache.logging.log4j:log4j-core:2.14.1") // Paper - implementation +- annotationProcessor("org.apache.logging.log4j:log4j-core:2.14.1") // Paper - Needed to generate meta for our Log4j plugins +- implementation("io.netty:netty-codec-haproxy:4.1.77.Final") // Paper - Add support for proxy protocol ++ implementation("org.apache.logging.log4j:log4j-core:2.19.0") // Paper - implementation ++ annotationProcessor("org.apache.logging.log4j:log4j-core:2.19.0") // Paper - Needed to generate meta for our Log4j plugins ++ implementation("io.netty:netty-codec-haproxy:4.1.85.Final") // Paper - Add support for proxy protocol + // Paper end + implementation("org.apache.logging.log4j:log4j-iostreams:2.19.0") // Paper - remove exclusion +- implementation("org.ow2.asm:asm:9.3") +- implementation("org.ow2.asm:asm-commons:9.3") // Paper - ASM event executor generation ++ implementation("org.ow2.asm:asm:9.4") ++ implementation("org.ow2.asm:asm-commons:9.4") // Paper - ASM event executor generation + implementation("org.spongepowered:configurate-yaml:4.1.2") // Paper - config files + implementation("commons-lang:commons-lang:2.6") + implementation("net.fabricmc:mapping-io:0.3.0") // Paper - needed to read mappings for stacktrace deobfuscation +@@ -37,27 +37,27 @@ dependencies { + isTransitive = false + } + // Paper end +- runtimeOnly("org.xerial:sqlite-jdbc:3.36.0.3") +- runtimeOnly("mysql:mysql-connector-java:8.0.29") ++ runtimeOnly("org.xerial:sqlite-jdbc:3.40.0.0") ++ runtimeOnly("mysql:mysql-connector-java:8.0.30") + runtimeOnly("com.lmax:disruptor:3.4.4") // Paper + + runtimeOnly("org.apache.maven:maven-resolver-provider:3.8.5") +- runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3") +- runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3") ++ runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.2") ++ runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.2") + + // Pufferfish start +- implementation("org.yaml:snakeyaml:1.32") +- implementation ("me.carleslc.Simple-YAML:Simple-Yaml:1.8.2") { ++ implementation("org.yaml:snakeyaml:1.33") ++ implementation ("me.carleslc.Simple-YAML:Simple-Yaml:1.8.3") { + exclude(group="org.yaml", module="snakeyaml") + } + // Pufferfish end + implementation("com.github.technove:Flare:34637f3f87") // Pufferfish - flare + +- testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test ++ testImplementation("io.github.classgraph:classgraph:4.8.152") // Paper - mob goal test + testImplementation("junit:junit:4.13.2") +- testImplementation("org.hamcrest:hamcrest-library:1.3") ++ testImplementation("org.hamcrest:hamcrest-library:2.2") + +- implementation("io.netty:netty-all:4.1.77.Final"); // Paper - Bump netty ++ implementation("io.netty:netty-all:4.1.85.Final"); // Paper - Bump netty + } + + val craftbukkitPackageVersion = "1_19_R2" // Paper +@@ -206,3 +206,6 @@ tasks.registerRunTask("runDev") { + description = "Spin up a non-relocated Mojang-mapped test server" + classpath(sourceSets.main.map { it.runtimeClasspath }) + } ++repositories { ++ mavenCentral() ++} diff --git a/patches/server/0004-Remove-Mojang-username-check.patch b/patches/server/0004-Remove-Mojang-username-check.patch new file mode 100644 index 00000000..5cddb3de --- /dev/null +++ b/patches/server/0004-Remove-Mojang-username-check.patch @@ -0,0 +1,49 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Wed, 12 Oct 2022 14:36:58 -0400 +Subject: [PATCH] Remove Mojang username check + + +diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +index f3b340fc6be9878d677a76673450aac3e0b73d4b..3011018c8fa221f515cf670dea9791f8c4ea1f69 100644 +--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +@@ -36,6 +36,7 @@ import net.minecraft.util.Crypt; + import net.minecraft.util.CryptException; + import net.minecraft.util.RandomSource; + import org.apache.commons.lang3.Validate; ++import org.dreeam.leaf.LeafConfig; + import org.slf4j.Logger; + + // CraftBukkit start +@@ -237,7 +238,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, + @Override + public void handleHello(ServerboundHelloPacket packet) { + Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet", new Object[0]); +- Validate.validState(ServerLoginPacketListenerImpl.isValidUsername(packet.name()), "Invalid characters in username", new Object[0]); ++ if (!LeafConfig.removeMojangUsernameCheck) Validate.validState(ServerLoginPacketListenerImpl.isValidUsername(packet.name()), "Invalid characters in username", new Object[0]); // Leaf - Remove Mojang's username check + // Paper start - validate usernames + if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.performUsernameValidation) { + if (!this.iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation && !validateUsername(packet.name())) { +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index 770553ef8976d76067396276792ea23a7ab7d51d..7870c85030144831936d692dde4fff7bedd692da 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -132,8 +132,17 @@ public class LeafConfig { + return config.getStringList(key); + } + ++ + public static String serverModName = "Leaf"; + private static void serverModName() { + serverModName = getString("settings.server-mod-name", serverModName); + } ++ ++ public static boolean removeMojangUsernameCheck; ++ private static void removeconfig() { ++ removeMojangUsernameCheck = getBoolean("remove-Mojang-username-check", true, ++ "Remove username check of Mojang", ++ "enabling all characters as username"); ++ ++ } + } diff --git a/patches/server/0005-Remove-Spigot-Check-for-Broken-BungeeCord-Configurat.patch b/patches/server/0005-Remove-Spigot-Check-for-Broken-BungeeCord-Configurat.patch new file mode 100644 index 00000000..11584715 --- /dev/null +++ b/patches/server/0005-Remove-Spigot-Check-for-Broken-BungeeCord-Configurat.patch @@ -0,0 +1,55 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Wed, 12 Oct 2022 14:48:45 -0400 +Subject: [PATCH] Remove Spigot Check for Broken BungeeCord Configurations + + +diff --git a/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java +index e7ff7ad3bf4dd17fdd34202ec3aef8e9512bc36d..a00c7ba6d2161ce91ade78e3d53c8d57f86e9c99 100644 +--- a/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java +@@ -9,6 +9,7 @@ import net.minecraft.network.protocol.handshake.ClientIntentionPacket; + import net.minecraft.network.protocol.handshake.ServerHandshakePacketListener; + import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; + import net.minecraft.server.MinecraftServer; ++import org.dreeam.leaf.LeafConfig; + + // CraftBukkit start + import java.net.InetAddress; +@@ -139,7 +140,9 @@ public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketL + { + connection.spoofedProfile = ServerHandshakePacketListenerImpl.gson.fromJson(split[3], com.mojang.authlib.properties.Property[].class); + } +- } else if ( ( split.length == 3 || split.length == 4 ) && ( ServerHandshakePacketListenerImpl.HOST_PATTERN.matcher( split[1] ).matches() ) ) { ++ // Leaf start - Remove Spigot check for broken BungeeCord configurations ++ } else if ( ( split.length == 3 || split.length == 4 ) && ( ServerHandshakePacketListenerImpl.HOST_PATTERN.matcher( split[1] ).matches() ) && !(LeafConfig.removeSpigotCheckBungeeConfig)) { ++ // Leaf end - Remove Spigot check for broken BungeeCord configurations + Component chatmessage = Component.literal("Unknown data in login hostname, did you forget to enable BungeeCord in spigot.yml?"); + this.connection.send(new ClientboundLoginDisconnectPacket(chatmessage)); + this.connection.disconnect(chatmessage); +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index 7870c85030144831936d692dde4fff7bedd692da..c107a0cab333b2b57931862073a4eb6832f755ab 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -132,17 +132,19 @@ public class LeafConfig { + return config.getStringList(key); + } + +- + public static String serverModName = "Leaf"; + private static void serverModName() { + serverModName = getString("settings.server-mod-name", serverModName); + } + + public static boolean removeMojangUsernameCheck; ++ public static boolean removeSpigotCheckBungeeConfig; + private static void removeconfig() { + removeMojangUsernameCheck = getBoolean("remove-Mojang-username-check", true, + "Remove username check of Mojang", + "enabling all characters as username"); +- ++ removeSpigotCheckBungeeConfig = getBoolean("remove-Spigot-check-bungee-config", true, ++ "Enable player enter backend server through proxy", ++ "without backend server enabling its bungee mode"); + } + } diff --git a/patches/server/0006-Remove-Paper-s-Fix-tripwire-state-inconsistency.patch b/patches/server/0006-Remove-Paper-s-Fix-tripwire-state-inconsistency.patch new file mode 100644 index 00000000..0233f2f4 --- /dev/null +++ b/patches/server/0006-Remove-Paper-s-Fix-tripwire-state-inconsistency.patch @@ -0,0 +1,66 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Sun, 6 Nov 2022 04:32:27 -0500 +Subject: [PATCH] Remove Paper's Fix tripwire state inconsistency + + +diff --git a/src/main/java/net/minecraft/world/level/block/TripWireBlock.java b/src/main/java/net/minecraft/world/level/block/TripWireBlock.java +index 7f60175bf671d282c11e9084670d2bb900968255..4e2fb4ee8e46b3c363992ff23e26f5a648c5f003 100644 +--- a/src/main/java/net/minecraft/world/level/block/TripWireBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/TripWireBlock.java +@@ -74,7 +74,7 @@ public class TripWireBlock extends Block { + @Override + public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean moved) { + if (!moved && !state.is(newState.getBlock())) { +- this.updateSource(world, pos, (BlockState) state.setValue(TripWireBlock.POWERED, true), true); // Paper - fix state inconsistency ++ this.updateSource(world, pos, (BlockState) state.setValue(TripWireBlock.POWERED, true)); + } + } + +@@ -89,12 +89,6 @@ public class TripWireBlock extends Block { + } + + private void updateSource(Level world, BlockPos pos, BlockState state) { +- // Paper start - fix state inconsistency +- this.updateSource(world, pos, state, false); +- } +- +- private void updateSource(Level world, BlockPos pos, BlockState state, boolean beingRemoved) { +- // Paper end + Direction[] aenumdirection = new Direction[]{Direction.SOUTH, Direction.WEST}; + int i = aenumdirection.length; + int j = 0; +@@ -110,7 +104,7 @@ public class TripWireBlock extends Block { + + if (iblockdata1.is((Block) this.hook)) { + if (iblockdata1.getValue(TripWireHookBlock.FACING) == enumdirection.getOpposite()) { +- this.hook.calculateState(world, blockposition1, iblockdata1, false, true, k, state, beingRemoved); // Paper - fix state inconsistency ++ this.hook.calculateState(world, blockposition1, iblockdata1, false, true, k, state); + } + } else if (iblockdata1.is((Block) this)) { + ++k; +diff --git a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java +index 004dce26ff073f1de52a84cd425c4f60fdab5e50..4a516828e5c6abd63511ee7c93fcff11203cf8d0 100644 +--- a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java +@@ -108,12 +108,6 @@ public class TripWireHookBlock extends Block { + } + + public void calculateState(Level world, BlockPos pos, BlockState state, boolean beingRemoved, boolean flag1, int i, @Nullable BlockState iblockdata1) { +- // Paper start - fix tripwire inconsistency +- this.calculateState(world, pos, state, beingRemoved, flag1, i, iblockdata1, false); +- } +- +- public void calculateState(Level world, BlockPos pos, BlockState state, boolean beingRemoved, boolean flag1, int i, @Nullable BlockState iblockdata1, boolean tripWireBeingRemoved) { +- // Paper end + Direction enumdirection = (Direction) state.getValue(TripWireHookBlock.FACING); + boolean flag2 = (Boolean) state.getValue(TripWireHookBlock.ATTACHED); + boolean flag3 = (Boolean) state.getValue(TripWireHookBlock.POWERED); +@@ -147,7 +141,6 @@ public class TripWireHookBlock extends Block { + boolean flag7 = (Boolean) iblockdata2.getValue(TripWireBlock.POWERED); + + flag5 |= flag6 && flag7; +- if (k != i || !tripWireBeingRemoved || !flag6) // Paper - don't update the tripwire again if being removed and not disarmed + aiblockdata[k] = iblockdata2; + if (k == i) { + world.scheduleTick(pos, (Block) this, 10); diff --git a/patches/server/0007-Remove-UseItemOnPacket-Too-Far-Check.patch b/patches/server/0007-Remove-UseItemOnPacket-Too-Far-Check.patch new file mode 100644 index 00000000..428799fd --- /dev/null +++ b/patches/server/0007-Remove-UseItemOnPacket-Too-Far-Check.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Fri, 18 Nov 2022 23:26:16 -0500 +Subject: [PATCH] Remove UseItemOnPacket Too Far Check + +This Check is added in 1.17.x -> 1.18.x update by Mojang. +By removing this check, it enable hackers to use some modules of hack clients. + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index aa23559719357f2678e3aa759d58ba4bde18bdd4..215eda414547bfdf662b4ea511fa3f774e125d94 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -182,6 +182,7 @@ import net.minecraft.world.phys.Vec3; + import net.minecraft.world.phys.shapes.BooleanOp; + import net.minecraft.world.phys.shapes.Shapes; + import net.minecraft.world.phys.shapes.VoxelShape; ++import org.dreeam.leaf.LeafConfig; + import org.slf4j.Logger; + + // CraftBukkit start +@@ -1952,7 +1953,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + Vec3 vec3d2 = vec3d.subtract(vec3d1); + double d0 = 1.0000001D; + +- if (Math.abs(vec3d2.x()) < 1.0000001D && Math.abs(vec3d2.y()) < 1.0000001D && Math.abs(vec3d2.z()) < 1.0000001D) { ++ if ((Math.abs(vec3d2.x()) < 1.0000001D && Math.abs(vec3d2.y()) < 1.0000001D && Math.abs(vec3d2.z()) < 1.0000001D) || LeafConfig.removeUseItemOnPacketTooFar) { // Leaf - Remove UseItemOnPacket Too Far Check + Direction enumdirection = movingobjectpositionblock.getDirection(); + + this.player.resetLastActionTime(); +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index c107a0cab333b2b57931862073a4eb6832f755ab..a221affdf87d28991248a4b83224194e222423e6 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -139,6 +139,8 @@ public class LeafConfig { + + public static boolean removeMojangUsernameCheck; + public static boolean removeSpigotCheckBungeeConfig; ++ public static boolean removeUseItemOnPacketTooFar; ++ + private static void removeconfig() { + removeMojangUsernameCheck = getBoolean("remove-Mojang-username-check", true, + "Remove username check of Mojang", +@@ -146,5 +148,7 @@ public class LeafConfig { + removeSpigotCheckBungeeConfig = getBoolean("remove-Spigot-check-bungee-config", true, + "Enable player enter backend server through proxy", + "without backend server enabling its bungee mode"); ++ removeUseItemOnPacketTooFar = getBoolean("remove-UseItemOnPacket-too-far-check", false, ++ "To enable this, players can use some packet modules with hack clients"); + } + } diff --git a/patches/server/0008-Purpur-Implement-TPSBar.patch b/patches/server/0008-Purpur-Implement-TPSBar.patch new file mode 100644 index 00000000..6f12e650 --- /dev/null +++ b/patches/server/0008-Purpur-Implement-TPSBar.patch @@ -0,0 +1,548 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sat, 12 Dec 2020 21:19:05 -0600 +Subject: [PATCH] Purpur: Implement TPSBar + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java +index 39844531b03eb8a6c70700b4ecbf0ff1a557424d..632ae75cb3bbc7a3955872d14ad0fbc2459f32e8 100644 +--- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java ++++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java +@@ -35,6 +35,7 @@ public abstract class CommandNode implements Comparable> { + private final boolean forks; + private Command command; + public LiteralCommandNode clientNode = null; // Paper ++ private String permission = null; public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } // Purpur + // CraftBukkit start + public void removeCommand(String name) { + this.children.remove(name); +diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java +index ae5dd08de75a7ed231295f306fd0974da3988249..b8d49f7607c646216d42f4e047997d47b49f228f 100644 +--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java ++++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java +@@ -317,6 +317,30 @@ public class CommandSourceStack implements SharedSuggestionProvider, com.destroy + } + } + ++ // Purpur start ++ public void sendSuccess(@Nullable String message) { ++ sendSuccess(message, false); ++ } ++ ++ public void sendSuccess(@Nullable String message, boolean broadcastToOps) { ++ if (message == null) { ++ return; ++ } ++ sendSuccess(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), broadcastToOps); ++ } ++ ++ public void sendSuccess(@Nullable net.kyori.adventure.text.Component message) { ++ sendSuccess(message, false); ++ } ++ ++ public void sendSuccess(@Nullable net.kyori.adventure.text.Component message, boolean broadcastToOps) { ++ if (message == null) { ++ return; ++ } ++ sendSuccess(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), broadcastToOps); ++ } ++ // Purpur end ++ + public void sendSuccess(Component message, boolean broadcastToOps) { + if (this.source.acceptsSuccess() && !this.silent) { + this.source.sendSystemMessage(message); +diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java +index 330f6c79417378da855326b4da665f9d240e748d..564830a1d511716ab977923ba43fa38c475f484f 100644 +--- a/src/main/java/net/minecraft/commands/Commands.java ++++ b/src/main/java/net/minecraft/commands/Commands.java +@@ -215,6 +215,7 @@ public class Commands { + SetPlayerIdleTimeoutCommand.register(this.dispatcher); + StopCommand.register(this.dispatcher); + WhitelistCommand.register(this.dispatcher); ++ org.dreeam.leaf.commands.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 3bed5eb755e97149a9651ca007564275e0eaf2d1..ab0a4a36241bee59b142df65736003c69dcb3dd4 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1022,6 +1022,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop cachedSingleHashSet; // Paper + public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper + public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event ++ private boolean tpsBar = false; // Purpur + + public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile) { + super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile); +@@ -508,6 +509,7 @@ public class ServerPlayer extends Player { + } + } + ++ if (nbt.contains("Purpur.TPSBar")) { this.tpsBar = nbt.getBoolean("Purpur.TPSBar"); } // Purpur + } + + @Override +@@ -574,6 +576,7 @@ public class ServerPlayer extends Player { + } + this.getBukkitEntity().setExtraData(nbt); // CraftBukkit + ++ nbt.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur + } + + // CraftBukkit start - World fallback code, either respawn location or global spawn +@@ -2550,4 +2553,14 @@ public class ServerPlayer extends Player { + return (CraftPlayer) super.getBukkitEntity(); + } + // CraftBukkit end ++ ++ // Purpur start ++ public boolean tpsBar() { ++ return this.tpsBar; ++ } ++ ++ public void tpsBar(boolean tpsBar) { ++ this.tpsBar = tpsBar; ++ } ++ // Purpur end + } +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index b348d33726b2b14ea2d12ce3430df2c0b94295f4..1895a3919d43e8e4f8399c75db8482df9e78bb3d 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -100,6 +100,7 @@ import net.minecraft.world.scores.Objective; + import net.minecraft.world.scores.PlayerTeam; + import net.minecraft.world.scores.Scoreboard; // Paper + import net.minecraft.world.scores.Team; ++import org.dreeam.leaf.tasks.BossBarTask; + import org.slf4j.Logger; + + // CraftBukkit start +@@ -511,6 +512,7 @@ public abstract class PlayerList { + scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam); + } + // Paper end ++ BossBarTask.addToAll(player); // Purpur + // CraftBukkit - Moved from above, added world + PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", player.getName().getString(), s1, player.getId(), worldserver1.serverLevelData.getLevelName(), player.getX(), player.getY(), player.getZ()); + } +@@ -621,6 +623,8 @@ public abstract class PlayerList { + } + public net.kyori.adventure.text.Component remove(ServerPlayer entityplayer, net.kyori.adventure.text.Component leaveMessage) { + // Paper end ++ BossBarTask.removeFromAll(entityplayer.getBukkitEntity()); // Purpur ++ + ServerLevel worldserver = entityplayer.getLevel(); + + entityplayer.awardStat(Stats.LEAVE_GAME); +diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +index 6035af2cf08353b3d3801220d8116d8611a0cd37..7774ab6a2e553a40def4bb4dceea9e5f58d31c1e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +@@ -94,6 +94,7 @@ public final class VanillaCommandWrapper extends BukkitCommand { + } + + public static String getPermission(CommandNode vanillaCommand) { ++ if (vanillaCommand.getPermission() != null) return vanillaCommand.getPermission(); // Purpur + // Paper start + final String commandName; + if (vanillaCommand.getRedirect() == null) { +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index a221affdf87d28991248a4b83224194e222423e6..a0bd1ec3f09959c7f72b484658c1c94a16818281 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -1,8 +1,10 @@ + package org.dreeam.leaf; + ++import net.kyori.adventure.bossbar.BossBar; + import net.minecraft.server.MinecraftServer; + import org.bukkit.configuration.ConfigurationSection; + import org.bukkit.configuration.MemoryConfiguration; ++import org.dreeam.leaf.tasks.TPSBarTask; + import org.jetbrains.annotations.Nullable; + import org.simpleyaml.configuration.comments.CommentType; + import org.simpleyaml.configuration.file.YamlFile; +@@ -149,6 +151,36 @@ public class LeafConfig { + "Enable player enter backend server through proxy", + "without backend server enabling its bungee mode"); + removeUseItemOnPacketTooFar = getBoolean("remove-UseItemOnPacket-too-far-check", false, +- "To enable this, players can use some packet modules with hack clients"); ++ "To enable this, players can use some packet modules with hack clients"); ++ } ++ ++ public static String tpsbarCommandOutput = "Tpsbar toggled for "; ++ ++ private static void messages() { ++ tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput); ++ } ++ ++ public static String commandTPSBarTitle = "TPS: MSPT: Ping: ms"; ++ public static BossBar.Overlay commandTPSBarProgressOverlay = BossBar.Overlay.NOTCHED_20; ++ public static TPSBarTask.FillMode commandTPSBarProgressFillMode = TPSBarTask.FillMode.MSPT; ++ public static BossBar.Color commandTPSBarProgressColorGood = BossBar.Color.GREEN; ++ public static BossBar.Color commandTPSBarProgressColorMedium = BossBar.Color.YELLOW; ++ public static BossBar.Color commandTPSBarProgressColorLow = BossBar.Color.RED; ++ public static String commandTPSBarTextColorGood = ""; ++ public static String commandTPSBarTextColorMedium = ""; ++ public static String commandTPSBarTextColorLow = ""; ++ public static int commandTPSBarTickInterval = 20; ++ ++ private static void commandSettings() { ++ commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle); ++ commandTPSBarProgressOverlay = BossBar.Overlay.valueOf(getString("settings.command.tpsbar.overlay", commandTPSBarProgressOverlay.name())); ++ commandTPSBarProgressFillMode = TPSBarTask.FillMode.valueOf(getString("settings.command.tpsbar.fill-mode", commandTPSBarProgressFillMode.name())); ++ commandTPSBarProgressColorGood = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.good", commandTPSBarProgressColorGood.name())); ++ commandTPSBarProgressColorMedium = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.medium", commandTPSBarProgressColorMedium.name())); ++ commandTPSBarProgressColorLow = BossBar.Color.valueOf(getString("settings.command.tpsbar.progress-color.low", commandTPSBarProgressColorLow.name())); ++ commandTPSBarTextColorGood = getString("settings.command.tpsbar.text-color.good", commandTPSBarTextColorGood); ++ commandTPSBarTextColorMedium = getString("settings.command.tpsbar.text-color.medium", commandTPSBarTextColorMedium); ++ commandTPSBarTextColorLow = getString("settings.command.tpsbar.text-color.low", commandTPSBarTextColorLow); ++ commandTPSBarTickInterval = getInt("settings.command.tpsbar.tick-interval", commandTPSBarTickInterval); + } + } +diff --git a/src/main/java/org/dreeam/leaf/commands/TPSBarCommand.java b/src/main/java/org/dreeam/leaf/commands/TPSBarCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..60742c5ecc99aa18d7292bd6df5e80441b19745b +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/commands/TPSBarCommand.java +@@ -0,0 +1,43 @@ ++package org.dreeam.leaf.commands; ++ ++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.dreeam.leaf.LeafConfig; ++import org.dreeam.leaf.tasks.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)) ++ .executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) ++ .then(Commands.argument("targets", EntityArgument.players()) ++ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) ++ ) ++ ).setPermission("bukkit.command.tpsbar"); ++ } ++ ++ 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(LeafConfig.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/dreeam/leaf/tasks/BossBarTask.java b/src/main/java/org/dreeam/leaf/tasks/BossBarTask.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ae629c689779339589de3ec28fb6a79bf54da583 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/tasks/BossBarTask.java +@@ -0,0 +1,109 @@ ++package org.dreeam.leaf.tasks; ++ ++import net.kyori.adventure.bossbar.BossBar; ++import net.minecraft.server.level.ServerPlayer; ++import org.bukkit.Bukkit; ++import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; ++import org.bukkit.entity.Player; ++import org.bukkit.scheduler.BukkitRunnable; ++ ++import java.util.HashMap; ++import java.util.HashSet; ++import java.util.Iterator; ++import java.util.Map; ++import java.util.UUID; ++ ++public abstract class BossBarTask extends BukkitRunnable { ++ private final Map bossbars = new HashMap<>(); ++ private boolean started; ++ ++ abstract BossBar createBossBar(); ++ ++ abstract void updateBossBar(BossBar bossbar, Player player); ++ ++ @Override ++ public void run() { ++ Iterator> iter = bossbars.entrySet().iterator(); ++ while (iter.hasNext()) { ++ Map.Entry entry = iter.next(); ++ Player player = Bukkit.getPlayer(entry.getKey()); ++ if (player == null) { ++ iter.remove(); ++ continue; ++ } ++ updateBossBar(entry.getValue(), player); ++ } ++ } ++ ++ @Override ++ public void cancel() { ++ super.cancel(); ++ new HashSet<>(this.bossbars.keySet()).forEach(uuid -> { ++ Player player = Bukkit.getPlayer(uuid); ++ if (player != null) { ++ removePlayer(player); ++ } ++ }); ++ this.bossbars.clear(); ++ } ++ ++ public boolean removePlayer(Player player) { ++ BossBar bossbar = this.bossbars.remove(player.getUniqueId()); ++ if (bossbar != null) { ++ player.hideBossBar(bossbar); ++ return true; ++ } ++ return false; ++ } ++ ++ public void addPlayer(Player player) { ++ removePlayer(player); ++ BossBar bossbar = createBossBar(); ++ this.bossbars.put(player.getUniqueId(), bossbar); ++ this.updateBossBar(bossbar, player); ++ player.showBossBar(bossbar); ++ } ++ ++ public boolean hasPlayer(UUID uuid) { ++ return this.bossbars.containsKey(uuid); ++ } ++ ++ public boolean togglePlayer(Player player) { ++ if (removePlayer(player)) { ++ return false; ++ } ++ addPlayer(player); ++ return true; ++ } ++ ++ public void start() { ++ stop(); ++ this.runTaskTimerAsynchronously(new MinecraftInternalPlugin(), 1, 1); ++ started = true; ++ } ++ ++ public void stop() { ++ if (started) { ++ cancel(); ++ } ++ } ++ ++ public static void startAll() { ++ TPSBarTask.instance().start(); ++ } ++ ++ public static void stopAll() { ++ TPSBarTask.instance().stop(); ++ } ++ ++ public static void addToAll(ServerPlayer player) { ++ Player bukkit = player.getBukkitEntity(); ++ if (player.tpsBar()) { ++ TPSBarTask.instance().addPlayer(bukkit); ++ } ++ } ++ ++ public static void removeFromAll(Player player) { ++ TPSBarTask.instance().removePlayer(player); ++ } ++} +diff --git a/src/main/java/org/dreeam/leaf/tasks/TPSBarTask.java b/src/main/java/org/dreeam/leaf/tasks/TPSBarTask.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9a184179d5968691a9c50be5536a7ce6b6c51dd0 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/tasks/TPSBarTask.java +@@ -0,0 +1,142 @@ ++package org.dreeam.leaf.tasks; ++ ++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.dreeam.leaf.LeafConfig; ++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(), LeafConfig.commandTPSBarProgressOverlay); ++ } ++ ++ @Override ++ void updateBossBar(BossBar bossbar, Player player) { ++ bossbar.progress(getBossBarProgress()); ++ bossbar.color(getBossBarColor()); ++ bossbar.name(MiniMessage.miniMessage().deserialize(LeafConfig.commandTPSBarTitle, ++ Placeholder.component("tps", getTPSColor()), ++ Placeholder.component("mspt", getMSPTColor()), ++ Placeholder.component("ping", getPingColor(player.getPing())) ++ )); ++ } ++ ++ @Override ++ public void run() { ++ if (++tick < LeafConfig.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 (LeafConfig.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(LeafConfig.commandTPSBarProgressFillMode)) { ++ return LeafConfig.commandTPSBarProgressColorGood; ++ } else if (isMedium(LeafConfig.commandTPSBarProgressFillMode)) { ++ return LeafConfig.commandTPSBarProgressColorMedium; ++ } else { ++ return LeafConfig.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 = LeafConfig.commandTPSBarTextColorGood; ++ } else if (isMedium(FillMode.TPS)) { ++ color = LeafConfig.commandTPSBarTextColorMedium; ++ } else { ++ color = LeafConfig.commandTPSBarTextColorLow; ++ } ++ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%.2f", tps))); ++ } ++ ++ private Component getMSPTColor() { ++ String color; ++ if (isGood(FillMode.MSPT)) { ++ color = LeafConfig.commandTPSBarTextColorGood; ++ } else if (isMedium(FillMode.MSPT)) { ++ color = LeafConfig.commandTPSBarTextColorMedium; ++ } else { ++ color = LeafConfig.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 = LeafConfig.commandTPSBarTextColorGood; ++ } else if (isMedium(FillMode.PING, ping)) { ++ color = LeafConfig.commandTPSBarTextColorMedium; ++ } else { ++ color = LeafConfig.commandTPSBarTextColorLow; ++ } ++ return MiniMessage.miniMessage().deserialize(color, Placeholder.parsed("text", String.format("%s", ping))); ++ } ++ ++ public enum FillMode { ++ TPS, MSPT, PING ++ } ++} diff --git a/patches/server/0009-Purpur-Add-compass-command.patch b/patches/server/0009-Purpur-Add-compass-command.patch new file mode 100644 index 00000000..f69f60dc --- /dev/null +++ b/patches/server/0009-Purpur-Add-compass-command.patch @@ -0,0 +1,234 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sat, 24 Jul 2021 00:07:31 -0500 +Subject: [PATCH] Purpur: Add compass command + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java +index 564830a1d511716ab977923ba43fa38c475f484f..2c5afab3cc0c66a2e63c1f647a033ca1a58f9e39 100644 +--- a/src/main/java/net/minecraft/commands/Commands.java ++++ b/src/main/java/net/minecraft/commands/Commands.java +@@ -216,6 +216,7 @@ public class Commands { + StopCommand.register(this.dispatcher); + WhitelistCommand.register(this.dispatcher); + org.dreeam.leaf.commands.TPSBarCommand.register(this.dispatcher); // Purpur ++ org.dreeam.leaf.commands.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 3db273840ecc03e7bf3e1a01f56d5128e4aeea06..5c67d38df288e789597613df99f779c1ddf40712 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -271,6 +271,7 @@ public class ServerPlayer extends Player { + public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper + public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event + private boolean tpsBar = false; // Purpur ++ private boolean compassBar = false; // Purpur + + public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile) { + super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile); +@@ -510,6 +511,7 @@ public class ServerPlayer extends Player { + } + + if (nbt.contains("Purpur.TPSBar")) { this.tpsBar = nbt.getBoolean("Purpur.TPSBar"); } // Purpur ++ if (nbt.contains("Purpur.CompassBar")) { this.compassBar = nbt.getBoolean("Purpur.CompassBar"); } // Purpur + } + + @Override +@@ -577,6 +579,7 @@ public class ServerPlayer extends Player { + this.getBukkitEntity().setExtraData(nbt); // CraftBukkit + + nbt.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur ++ nbt.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur + } + + // CraftBukkit start - World fallback code, either respawn location or global spawn +@@ -2562,5 +2565,13 @@ public class ServerPlayer extends Player { + public void tpsBar(boolean tpsBar) { + this.tpsBar = tpsBar; + } ++ ++ public boolean compassBar() { ++ return this.compassBar; ++ } ++ ++ public void compassBar(boolean compassBar) { ++ this.compassBar = compassBar; ++ } + // Purpur end + } +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index a0bd1ec3f09959c7f72b484658c1c94a16818281..e56151fe0dbc0c867dff728a55daa35c7714d3a6 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -170,6 +170,11 @@ public class LeafConfig { + 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; + + private static void commandSettings() { + commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle); +@@ -182,5 +187,17 @@ public class LeafConfig { + 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); ++ ++ } ++ ++ public static boolean compassItemShowsBossBar = false; ++ private static void itemSettings() { ++ compassItemShowsBossBar = getBoolean("gameplay-mechanics.item.compass.holding-shows-bossbar", compassItemShowsBossBar); + } + } +diff --git a/src/main/java/org/dreeam/leaf/commands/CompassCommand.java b/src/main/java/org/dreeam/leaf/commands/CompassCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a3c85794307e906c4343d490e0e10daaab0efb38 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/commands/CompassCommand.java +@@ -0,0 +1,27 @@ ++package org.dreeam.leaf.commands; ++ ++import com.mojang.brigadier.CommandDispatcher; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.commands.Commands; ++import net.minecraft.server.level.ServerPlayer; ++import org.dreeam.leaf.tasks.CompassTask; ++ ++public class CompassCommand { ++ public static void register(CommandDispatcher dispatcher) { ++ dispatcher.register(Commands.literal("compass") ++ .requires(listener -> listener.hasPermission(2)) ++ .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; ++ }) ++ ).setPermission("bukkit.command.compass"); ++ } ++} +diff --git a/src/main/java/org/dreeam/leaf/tasks/BossBarTask.java b/src/main/java/org/dreeam/leaf/tasks/BossBarTask.java +index ae629c689779339589de3ec28fb6a79bf54da583..7dacf4f21d082dd9a19db6e5122d06b5bda8fc95 100644 +--- a/src/main/java/org/dreeam/leaf/tasks/BossBarTask.java ++++ b/src/main/java/org/dreeam/leaf/tasks/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/dreeam/leaf/tasks/CompassTask.java b/src/main/java/org/dreeam/leaf/tasks/CompassTask.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3ecd9bb435e5066176da88f7d50d4920cee017e5 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/tasks/CompassTask.java +@@ -0,0 +1,68 @@ ++package org.dreeam.leaf.tasks; ++ ++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.dreeam.leaf.LeafConfig; ++ ++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 < LeafConfig.commandCompassBarTickInterval) { ++ return; ++ } ++ tick = 0; ++ ++ MinecraftServer.getServer().getAllLevels().forEach((level) -> { ++ if (LeafConfig.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(""), LeafConfig.commandCompassBarProgressPercent, LeafConfig.commandCompassBarProgressColor, LeafConfig.commandCompassBarProgressOverlay); ++ } ++ ++ @Override ++ void updateBossBar(BossBar bossbar, Player player) { ++ float yaw = player.getLocation().getYaw(); ++ int length = LeafConfig.commandCompassBarTitle.length(); ++ int pos = (int) ((normalize(yaw) * (length / 720F)) + (length / 2F)); ++ bossbar.name(Component.text(LeafConfig.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/0010-Purpur-Implement-ram-and-rambar-commands.patch b/patches/server/0010-Purpur-Implement-ram-and-rambar-commands.patch new file mode 100644 index 00000000..a4bbd748 --- /dev/null +++ b/patches/server/0010-Purpur-Implement-ram-and-rambar-commands.patch @@ -0,0 +1,345 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Mon, 26 Sep 2022 07:43:30 -0500 +Subject: [PATCH] Purpur: Implement ram and rambar commands + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java +index 2c5afab3cc0c66a2e63c1f647a033ca1a58f9e39..8ee8ff15f4511684261ff7a9417baa2ccd313b69 100644 +--- a/src/main/java/net/minecraft/commands/Commands.java ++++ b/src/main/java/net/minecraft/commands/Commands.java +@@ -217,6 +217,8 @@ public class Commands { + WhitelistCommand.register(this.dispatcher); + org.dreeam.leaf.commands.TPSBarCommand.register(this.dispatcher); // Purpur + org.dreeam.leaf.commands.CompassCommand.register(this.dispatcher); // Purpur ++ org.dreeam.leaf.commands.RamBarCommand.register(this.dispatcher); // Purpur ++ org.dreeam.leaf.commands.RamCommand.register(this.dispatcher); // Purpur + } + + if (environment.includeIntegrated) { +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 5c67d38df288e789597613df99f779c1ddf40712..95db6657cd65b71f994f0221b0c46be4a1f8e10b 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -272,6 +272,7 @@ public class ServerPlayer extends Player { + public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event + private boolean tpsBar = false; // Purpur + private boolean compassBar = false; // Purpur ++ private boolean ramBar = false; // Purpur + + public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile) { + super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile); +@@ -510,6 +511,7 @@ public class ServerPlayer extends Player { + } + } + ++ if (nbt.contains("Purpur.RamBar")) { this.ramBar = nbt.getBoolean("Purpur.RamBar"); } // Purpur + if (nbt.contains("Purpur.TPSBar")) { this.tpsBar = nbt.getBoolean("Purpur.TPSBar"); } // Purpur + if (nbt.contains("Purpur.CompassBar")) { this.compassBar = nbt.getBoolean("Purpur.CompassBar"); } // Purpur + } +@@ -578,6 +580,7 @@ public class ServerPlayer extends Player { + } + this.getBukkitEntity().setExtraData(nbt); // CraftBukkit + ++ nbt.putBoolean("Purpur.RamBar", this.ramBar); // Purpur + nbt.putBoolean("Purpur.TPSBar", this.tpsBar); // Purpur + nbt.putBoolean("Purpur.CompassBar", this.compassBar); // Purpur + } +@@ -2573,5 +2576,14 @@ public class ServerPlayer extends Player { + 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/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index e56151fe0dbc0c867dff728a55daa35c7714d3a6..afdee99c7d5ecedf8d2f6856f0aa9271c6bbb23d 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -175,6 +175,17 @@ public class LeafConfig { + public static BossBar.Color commandCompassBarProgressColor = BossBar.Color.BLUE; + public static float commandCompassBarProgressPercent = 1.0F; + public static int commandCompassBarTickInterval = 5; ++ public static String ramCommandOutput = "Ram Usage: / ()"; ++ public static String rambarCommandOutput = "Rambar toggled for "; ++ 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; + + private static void commandSettings() { + commandTPSBarTitle = getString("settings.command.tpsbar.title", commandTPSBarTitle); +@@ -194,6 +205,17 @@ public class LeafConfig { + commandCompassBarProgressPercent = (float) getDouble("settings.command.compass.percent", commandCompassBarProgressPercent); + commandCompassBarTickInterval = getInt("settings.command.compass.tick-interval", commandCompassBarTickInterval); + ++ ramCommandOutput = getString("settings.messages.ram-command-output", ramCommandOutput); ++ rambarCommandOutput = getString("settings.messages.rambar-command-output", rambarCommandOutput); ++ 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); + } + + public static boolean compassItemShowsBossBar = false; +diff --git a/src/main/java/org/dreeam/leaf/commands/RamBarCommand.java b/src/main/java/org/dreeam/leaf/commands/RamBarCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..084be8ead4845af5669dbbebc3ebc80facb3e454 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/commands/RamBarCommand.java +@@ -0,0 +1,43 @@ ++package org.dreeam.leaf.commands; ++ ++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.dreeam.leaf.LeafConfig; ++import org.dreeam.leaf.tasks.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)) ++ .executes(context -> execute(context.getSource(), Collections.singleton(context.getSource().getPlayerOrException()))) ++ .then(Commands.argument("targets", EntityArgument.players()) ++ .executes((context) -> execute(context.getSource(), EntityArgument.getPlayers(context, "targets"))) ++ ) ++ ).setPermission("bukkit.command.rambar"); ++ } ++ ++ 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(LeafConfig.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/dreeam/leaf/commands/RamCommand.java b/src/main/java/org/dreeam/leaf/commands/RamCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6cfb2791afed4122782ae5a8a5a9ecef698d8823 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/commands/RamCommand.java +@@ -0,0 +1,30 @@ ++package org.dreeam.leaf.commands; ++ ++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.dreeam.leaf.LeafConfig; ++import org.dreeam.leaf.tasks.RamBarTask; ++ ++public class RamCommand { ++ public static void register(CommandDispatcher dispatcher) { ++ dispatcher.register(Commands.literal("ram") ++ .requires(listener -> listener.hasPermission(2)) ++ .executes(context -> { ++ CommandSourceStack sender = context.getSource(); ++ RamBarTask ramBar = RamBarTask.instance(); ++ sender.sendSuccess(PaperAdventure.asVanilla(MiniMessage.miniMessage().deserialize(LeafConfig.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; ++ }) ++ ).setPermission("bukkit.command.ram"); ++ } ++} +diff --git a/src/main/java/org/dreeam/leaf/tasks/BossBarTask.java b/src/main/java/org/dreeam/leaf/tasks/BossBarTask.java +index 7dacf4f21d082dd9a19db6e5122d06b5bda8fc95..46644c72bf1db3cb86ffbf50dd7d723cb04e04e3 100644 +--- a/src/main/java/org/dreeam/leaf/tasks/BossBarTask.java ++++ b/src/main/java/org/dreeam/leaf/tasks/BossBarTask.java +@@ -91,11 +91,13 @@ public abstract class BossBarTask extends BukkitRunnable { + public static void startAll() { + TPSBarTask.instance().start(); + CompassTask.instance().start(); ++ RamBarTask.instance().start(); + } + + public static void stopAll() { + TPSBarTask.instance().stop(); + CompassTask.instance().stop(); ++ RamBarTask.instance().stop(); + } + + public static void addToAll(ServerPlayer player) { +@@ -106,10 +108,14 @@ public abstract class BossBarTask extends BukkitRunnable { + if (player.compassBar()) { + CompassTask.instance().addPlayer(bukkit); + } ++ if (player.ramBar()) { ++ RamBarTask.instance().addPlayer(bukkit); ++ } + } + + public static void removeFromAll(Player player) { + TPSBarTask.instance().removePlayer(player); + CompassTask.instance().removePlayer(player); ++ RamBarTask.instance().removePlayer(player); + } + } +diff --git a/src/main/java/org/dreeam/leaf/tasks/RamBarTask.java b/src/main/java/org/dreeam/leaf/tasks/RamBarTask.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c0d2f4f9fdd60f89376a18c88cf82012acd62da0 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/tasks/RamBarTask.java +@@ -0,0 +1,117 @@ ++package org.dreeam.leaf.tasks; ++ ++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.dreeam.leaf.LeafConfig; ++ ++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(), LeafConfig.commandRamBarProgressOverlay); ++ } ++ ++ @Override ++ void updateBossBar(BossBar bossbar, Player player) { ++ bossbar.progress(getBossBarProgress()); ++ bossbar.color(getBossBarColor()); ++ bossbar.name(MiniMessage.miniMessage().deserialize(LeafConfig.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 < LeafConfig.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 LeafConfig.commandRamBarProgressColorGood; ++ } else if (this.percent < 0.75F) { ++ return LeafConfig.commandRamBarProgressColorMedium; ++ } else { ++ return LeafConfig.commandRamBarProgressColorLow; ++ } ++ } ++ ++ public Component format(long v) { ++ String color; ++ if (this.percent < 0.60F) { ++ color = LeafConfig.commandRamBarTextColorGood; ++ } else if (this.percent < 0.85F) { ++ color = LeafConfig.commandRamBarTextColorMedium; ++ } else { ++ color = LeafConfig.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/0011-Purpur-Lagging-threshold.patch b/patches/server/0011-Purpur-Lagging-threshold.patch new file mode 100644 index 00000000..515e666f --- /dev/null +++ b/patches/server/0011-Purpur-Lagging-threshold.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Tue, 23 Jul 2019 10:07:16 -0500 +Subject: [PATCH] Purpur: Lagging threshold + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index ab0a4a36241bee59b142df65736003c69dcb3dd4..411fae7132b1d1763712594a18cff10d612cef7e 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -305,6 +305,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop +Date: Thu, 26 Mar 2020 19:06:22 -0500 +Subject: [PATCH] Purpur: Configurable TPS Catchup + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 411fae7132b1d1763712594a18cff10d612cef7e..75daffc4eaa831708f1ccfa5370ddf1871998033 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1179,6 +1179,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop +Date: Sun, 5 Jul 2020 23:40:16 -0500 +Subject: [PATCH] Purpur: Add allow water in end world option + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/world/item/BucketItem.java b/src/main/java/net/minecraft/world/item/BucketItem.java +index 5c6aa9c464784ad5ee366412d080c72d3d22a76f..35d819aa2e660263cf6a5a0c09a6e73f7a89389b 100644 +--- a/src/main/java/net/minecraft/world/item/BucketItem.java ++++ b/src/main/java/net/minecraft/world/item/BucketItem.java +@@ -166,7 +166,7 @@ public class BucketItem extends Item implements DispensibleContainerItem { + // CraftBukkit end + if (!flag1) { + return movingobjectpositionblock != null && this.emptyContents(entityhuman, world, movingobjectpositionblock.getBlockPos().relative(movingobjectpositionblock.getDirection()), (BlockHitResult) null, enumdirection, clicked, itemstack, enumhand); // CraftBukkit +- } else if (world.dimensionType().ultraWarm() && this.content.is(FluidTags.WATER)) { ++ } else if ((world.dimensionType().ultraWarm() || (world.isTheEnd() && !org.dreeam.leaf.LeafConfig.allowWaterPlacementInTheEnd)) && this.content.is(FluidTags.WATER)) { // Purpur + int i = blockposition.getX(); + int j = blockposition.getY(); + int k = blockposition.getZ(); +@@ -174,7 +174,7 @@ public class BucketItem extends Item implements DispensibleContainerItem { + world.playSound(entityhuman, blockposition, SoundEvents.FIRE_EXTINGUISH, SoundSource.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F); + + for (int l = 0; l < 8; ++l) { +- world.addParticle(ParticleTypes.LARGE_SMOKE, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 0.0D, 0.0D, 0.0D); ++ ((ServerLevel) world).sendParticles(null, ParticleTypes.LARGE_SMOKE, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 1, 0.0D, 0.0D, 0.0D, 0.0D, true); // Purpur + } + + return true; +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index edd2c236ca7c37e1a3aec0048b8974f4cd62f2cc..62aff29485f124880739e7d2b5fb0d8a9d957741 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -1635,4 +1635,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + return null; + } + // Paper end ++ ++ // Purpur start ++ public boolean isNether() { ++ return getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER; ++ } ++ ++ public boolean isTheEnd() { ++ return getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END; ++ } ++ // Purpur end + } +diff --git a/src/main/java/net/minecraft/world/level/block/IceBlock.java b/src/main/java/net/minecraft/world/level/block/IceBlock.java +index 64206d94a5bf210116d208f9678618b905a61428..fbb06c5db6f80008a54563b7d959e55e5ba96673 100644 +--- a/src/main/java/net/minecraft/world/level/block/IceBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/IceBlock.java +@@ -31,7 +31,7 @@ public class IceBlock extends HalfTransparentBlock { + public void afterDestroy(Level world, BlockPos pos, ItemStack stack) { + // Paper end + if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, stack) == 0) { +- if (world.dimensionType().ultraWarm()) { ++ if (world.isNether() || (world.isTheEnd() && !org.dreeam.leaf.LeafConfig.allowWaterPlacementInTheEnd)) { // Purpur + world.removeBlock(pos, false); + return; + } +@@ -59,7 +59,7 @@ public class IceBlock extends HalfTransparentBlock { + return; + } + // CraftBukkit end +- if (world.dimensionType().ultraWarm()) { ++ if (world.isNether() || (world.isTheEnd() && !org.dreeam.leaf.LeafConfig.allowWaterPlacementInTheEnd)) { // Purpur + world.removeBlock(pos, false); + } else { + world.setBlockAndUpdate(pos, Blocks.WATER.defaultBlockState()); +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index 66092bc87472278898743971dd80244cb750297e..06664bb40fe4b55998e7a0957cbb4b9fa4f8b05f 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -232,4 +232,9 @@ public class LeafConfig { + private static void tpsCatchup() { + tpsCatchup = getBoolean("settings.tps-catchup", tpsCatchup); + } ++ ++ public static boolean allowWaterPlacementInTheEnd = true; ++ private static void allowWaterPlacementInEnd() { ++ allowWaterPlacementInTheEnd = getBoolean("settings.allow-water-placement-in-the-end", allowWaterPlacementInTheEnd); ++ } + } diff --git a/patches/server/0014-Purpur-Configurable-sponge-absorption.patch b/patches/server/0014-Purpur-Configurable-sponge-absorption.patch new file mode 100644 index 00000000..abbab3f4 --- /dev/null +++ b/patches/server/0014-Purpur-Configurable-sponge-absorption.patch @@ -0,0 +1,63 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Encode42 +Date: Wed, 24 Mar 2021 20:30:37 -0400 +Subject: [PATCH] Purpur: Configurable sponge absorption + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +Allows the total area and radius of water blocks the sponge can absorb to be changed. + +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 7304b2659eb45bc4bc9fa7c43e6ca07221d0fc73..35a69bb50bc0575dd4f285cc9499d085fe6512ea 100644 +--- a/src/main/java/net/minecraft/world/level/block/SpongeBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/SpongeBlock.java +@@ -76,13 +76,13 @@ public class SpongeBlock extends Block { + if (fluid.is(FluidTags.WATER)) { + if (iblockdata.getBlock() instanceof BucketPickup && !((BucketPickup) iblockdata.getBlock()).pickupBlock(blockList, blockposition2, iblockdata).isEmpty()) { // CraftBukkit + ++i; +- if (j < 6) { ++ if (j < org.dreeam.leaf.LeafConfig.spongeAbsorptionRadius) { // Purpur + queue.add(new Tuple<>(blockposition2, j + 1)); + } + } else if (iblockdata.getBlock() instanceof LiquidBlock) { + blockList.setBlock(blockposition2, Blocks.AIR.defaultBlockState(), 3); // CraftBukkit + ++i; +- if (j < 6) { ++ if (j < org.dreeam.leaf.LeafConfig.spongeAbsorptionRadius) { // Purpur + queue.add(new Tuple<>(blockposition2, j + 1)); + } + } else if (material == Material.WATER_PLANT || material == Material.REPLACEABLE_WATER_PLANT) { +@@ -93,14 +93,14 @@ public class SpongeBlock extends Block { + blockList.setBlock(blockposition2, Blocks.AIR.defaultBlockState(), 3); + // CraftBukkit end + ++i; +- if (j < 6) { ++ if (j < org.dreeam.leaf.LeafConfig.spongeAbsorptionRadius) { // Purpur + queue.add(new Tuple<>(blockposition2, j + 1)); + } + } + } + } + +- if (i > 64) { ++ if (i > org.dreeam.leaf.LeafConfig.spongeAbsorptionArea) { // Purpur + break; + } + } +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index 06664bb40fe4b55998e7a0957cbb4b9fa4f8b05f..680ada855140ad86c5c953c7357fc745b308b334 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -237,4 +237,11 @@ public class LeafConfig { + private static void allowWaterPlacementInEnd() { + allowWaterPlacementInTheEnd = getBoolean("settings.allow-water-placement-in-the-end", allowWaterPlacementInTheEnd); + } ++ ++ public static int spongeAbsorptionArea = 64; ++ public static int spongeAbsorptionRadius = 6; ++ private static void spongeSettings() { ++ spongeAbsorptionArea = getInt("blocks.sponge.absorption.area", spongeAbsorptionArea); ++ spongeAbsorptionRadius = getInt("blocks.sponge.absorption.radius", spongeAbsorptionRadius); ++ } + } diff --git a/patches/server/0015-Purpur-Configurable-piston-push-limit.patch b/patches/server/0015-Purpur-Configurable-piston-push-limit.patch new file mode 100644 index 00000000..214b2aa6 --- /dev/null +++ b/patches/server/0015-Purpur-Configurable-piston-push-limit.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: DoctaEnkoda +Date: Sun, 2 May 2021 23:14:54 +0200 +Subject: [PATCH] Purpur: Configurable piston push limit + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java b/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java +index 744d91546d1a810f60a43c15ed74b4158f341a4a..460609a50d0f68c191f0fd529a7dc582ac0a2317 100644 +--- a/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java ++++ b/src/main/java/net/minecraft/world/level/block/piston/PistonStructureResolver.java +@@ -86,7 +86,7 @@ public class PistonStructureResolver { + return true; + } else { + int i = 1; +- if (i + this.toPush.size() > 12) { ++ if (i + this.toPush.size() > org.dreeam.leaf.LeafConfig.pistonBlockPushLimit) { // Purpur + return false; + } else { + while(isSticky(blockState)) { +@@ -98,7 +98,7 @@ public class PistonStructureResolver { + } + + ++i; +- if (i + this.toPush.size() > 12) { ++ if (i + this.toPush.size() > org.dreeam.leaf.LeafConfig.pistonBlockPushLimit) { // Purpur + return false; + } + } +@@ -142,7 +142,7 @@ public class PistonStructureResolver { + return true; + } + +- if (this.toPush.size() >= 12) { ++ if (this.toPush.size() >= org.dreeam.leaf.LeafConfig.pistonBlockPushLimit) { // Purpur + return false; + } + +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index 680ada855140ad86c5c953c7357fc745b308b334..e04d0dc3ed108edee9b8cabea56f86556ac9fb78 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -244,4 +244,9 @@ public class LeafConfig { + spongeAbsorptionArea = getInt("blocks.sponge.absorption.area", spongeAbsorptionArea); + spongeAbsorptionRadius = getInt("blocks.sponge.absorption.radius", spongeAbsorptionRadius); + } ++ ++ public static int pistonBlockPushLimit = 12; ++ private static void pistonSettings() { ++ pistonBlockPushLimit = getInt("blocks.piston.block-push-limit", pistonBlockPushLimit); ++ } + } diff --git a/patches/server/0016-Purpur-Configurable-broadcast-settings.patch b/patches/server/0016-Purpur-Configurable-broadcast-settings.patch new file mode 100644 index 00000000..1e829cf5 --- /dev/null +++ b/patches/server/0016-Purpur-Configurable-broadcast-settings.patch @@ -0,0 +1,145 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: DoctaEnkoda +Date: Mon, 3 May 2021 01:33:14 +0200 +Subject: [PATCH] Purpur: Configurable broadcast settings + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java +index a0c19503aabab5378d672a30163d35a5ba05b6c1..0b089a2032d468532a40e81cb0e9eda4cf184ad0 100644 +--- a/src/main/java/net/minecraft/server/PlayerAdvancements.java ++++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java +@@ -297,6 +297,7 @@ public class PlayerAdvancements { + advancement.getRewards().grant(this.player); + // Paper start - Add Adventure message to PlayerAdvancementDoneEvent + if (message != null && this.player.level.getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) { ++ if (org.dreeam.leaf.LeafConfig.advancementOnlyBroadcastToAffectedPlayer) this.player.sendMessage(message); else // Purpur + this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), false); + // Paper end + } +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 95db6657cd65b71f994f0221b0c46be4a1f8e10b..0ff21883cff37d1df7fda9b2cb93abcfd43f6482 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -949,6 +949,7 @@ public class ServerPlayer extends Player { + })); + Team scoreboardteambase = this.getTeam(); + ++ if (org.dreeam.leaf.LeafConfig.deathMessageOnlyBroadcastToAffectedPlayer) this.sendSystemMessage(ichatbasecomponent); else // Purpur + if (scoreboardteambase != null && scoreboardteambase.getDeathMessageVisibility() != Team.Visibility.ALWAYS) { + if (scoreboardteambase.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) { + this.server.getPlayerList().broadcastSystemToTeam(this, ichatbasecomponent); +@@ -1749,6 +1750,26 @@ public class ServerPlayer extends Player { + this.lastSentExp = -1; // CraftBukkit - Added to reset + } + ++ // Purpur start ++ public void sendActionBarMessage(@Nullable String message) { ++ if (message != null && !message.isEmpty()) { ++ sendActionBarMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message)); ++ } ++ } ++ ++ public void sendActionBarMessage(@Nullable net.kyori.adventure.text.Component message) { ++ if (message != null) { ++ sendActionBarMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message)); ++ } ++ } ++ ++ public void sendActionBarMessage(@Nullable Component message) { ++ if (message != null) { ++ displayClientMessage(message, true); ++ } ++ } ++ // Purpur end ++ + @Override + public void displayClientMessage(Component message, boolean overlay) { + this.sendSystemMessage(message, overlay); +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 1895a3919d43e8e4f8399c75db8482df9e78bb3d..7b731d38c06a525d9a88f88ee75f32d159afdedc 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -1088,6 +1088,20 @@ public abstract class PlayerList { + } + // CraftBukkit end + ++ // Purpur Start ++ public void broadcastMiniMessage(@Nullable String message, boolean overlay) { ++ if (message != null && !message.isEmpty()) { ++ this.broadcastMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message), overlay); ++ } ++ } ++ ++ public void broadcastMessage(@Nullable net.kyori.adventure.text.Component message, boolean overlay) { ++ if (message != null) { ++ this.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), overlay); ++ } ++ } ++ // Purpur end ++ + public void broadcastAll(Packet packet, ResourceKey dimension) { + Iterator iterator = this.players.iterator(); + +diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java +index 8795c94e6b6474addddbb0b337a962e8fac46b2b..f2ef4d93e070167d70f597e8893bf29b53433a28 100644 +--- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java ++++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java +@@ -256,6 +256,15 @@ public class DamageSource { + return entityliving1 != null ? Component.translatable(s1, entity.getDisplayName(), entityliving1.getDisplayName()) : Component.translatable(s, entity.getDisplayName()); + } + ++ // Purpur start ++ public Component getLocalizedDeathMessage(String str, LivingEntity entity) { ++ net.kyori.adventure.text.Component name = io.papermc.paper.adventure.PaperAdventure.asAdventure(entity.getDisplayName()); ++ net.kyori.adventure.text.minimessage.tag.resolver.TagResolver template = net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component("player", name); ++ net.kyori.adventure.text.Component component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(str, template); ++ return io.papermc.paper.adventure.PaperAdventure.asVanilla(component); ++ } ++ // Purpur end ++ + public boolean isFire() { + return this.isFireSource; + } +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 374b46c344ca868cab7c47be69cb005eb90319e8..37f1e4aa89da81fcae2d38bb741f3386950bdf51 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -3853,6 +3853,20 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + return SlotAccess.NULL; + } + ++ // Purpur Start ++ public void sendMiniMessage(@Nullable String message) { ++ if (message != null && !message.isEmpty()) { ++ this.sendMessage(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(message)); ++ } ++ } ++ ++ public void sendMessage(@Nullable net.kyori.adventure.text.Component message) { ++ if (message != null) { ++ this.sendSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message)); ++ } ++ } ++ // Purpur end ++ + @Override + public void sendSystemMessage(Component message) {} + +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index e04d0dc3ed108edee9b8cabea56f86556ac9fb78..040c1f584c525990ec844cba9f01df5b33fa2a37 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -249,4 +249,11 @@ public class LeafConfig { + private static void pistonSettings() { + pistonBlockPushLimit = getInt("blocks.piston.block-push-limit", pistonBlockPushLimit); + } ++ ++ public static boolean advancementOnlyBroadcastToAffectedPlayer = false; ++ public static boolean deathMessageOnlyBroadcastToAffectedPlayer = false; ++ private static void broadcastSettings() { ++ advancementOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.advancement.only-broadcast-to-affected-player", advancementOnlyBroadcastToAffectedPlayer); ++ deathMessageOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.death.only-broadcast-to-affected-player", deathMessageOnlyBroadcastToAffectedPlayer); ++ } + } diff --git a/patches/server/0017-Purpur-Allow-player-join-full-server-by-permission.patch b/patches/server/0017-Purpur-Allow-player-join-full-server-by-permission.patch new file mode 100644 index 00000000..88483e34 --- /dev/null +++ b/patches/server/0017-Purpur-Allow-player-join-full-server-by-permission.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: DoctaEnkoda +Date: Thu, 24 Jun 2021 02:28:32 +0200 +Subject: [PATCH] Purpur: Allow player join full server by permission + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 7b731d38c06a525d9a88f88ee75f32d159afdedc..37a46d95793b43f5cb6ade9e2f2d20bf0942f21e 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -792,7 +792,7 @@ public abstract class PlayerList { + event.disallow(PlayerLoginEvent.Result.KICK_BANNED, PaperAdventure.asAdventure(ichatmutablecomponent)); // Paper - Adventure + } else { + // return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameprofile) ? IChatBaseComponent.translatable("multiplayer.disconnect.server_full") : null; +- if (this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameprofile)) { ++ if (this.players.size() >= this.maxPlayers && !(player.hasPermission("purpur.joinfullserver") || this.canBypassPlayerLimit(gameprofile))) { // Purpur + event.disallow(PlayerLoginEvent.Result.KICK_FULL, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.serverFullMessage)); // Spigot // Paper - Adventure + } + } diff --git a/patches/server/0018-Purpur-Halloween-options-and-optimizations.patch b/patches/server/0018-Purpur-Halloween-options-and-optimizations.patch new file mode 100644 index 00000000..dbec437a --- /dev/null +++ b/patches/server/0018-Purpur-Halloween-options-and-optimizations.patch @@ -0,0 +1,79 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: DoctaEnkoda +Date: Mon, 13 Sep 2021 04:48:21 +0200 +Subject: [PATCH] Purpur: Halloween options and optimizations + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +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 1572a81ce1718964d795f2a2a411402f88901c73..ae2c84af8baaf1bba9b0debe3774321417d77413 100644 +--- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java ++++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java +@@ -246,7 +246,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; +@@ -260,6 +260,7 @@ public class Bat extends AmbientCreature { + private static boolean isSpookySeason = false; + private static final int ONE_HOUR = 20 * 60 * 60; + private static int lastSpookyCheck = -ONE_HOUR; ++ public static boolean isHalloweenSeason(Level level) { return org.dreeam.leaf.LeafConfig.forceHalloweenSeason || isHalloween(); } // Purpur + private static boolean isHalloween() { + if (net.minecraft.server.MinecraftServer.currentTick - lastSpookyCheck > ONE_HOUR) { + LocalDate localdate = LocalDate.now(); +diff --git a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java +index b8abee145fc92faddef98da913eca7715b6bfc03..41de63fdcfe48c2a77f2d04e5bffbca41c98afe3 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java ++++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java +@@ -158,11 +158,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo + this.reassessWeaponGoal(); + this.setCanPickUpLoot(this.level.paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.skeletons || randomsource.nextFloat() < 0.55F * difficulty.getSpecialMultiplier()); // Paper + if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { +- LocalDate localdate = LocalDate.now(); +- int i = localdate.get(ChronoField.DAY_OF_MONTH); +- int j = localdate.get(ChronoField.MONTH_OF_YEAR); +- +- if (j == 10 && i == 31 && randomsource.nextFloat() < 0.25F) { ++ if (net.minecraft.world.entity.ambient.Bat.isHalloweenSeason(world.getMinecraftWorld()) && this.random.nextFloat() < org.dreeam.leaf.LeafConfig.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 9976205537cfe228735687f1e9c52c74ac025690..dfe4c6d2bf9bee2e019635d02f01dc08f99c160e 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java +@@ -559,11 +559,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() < org.dreeam.leaf.LeafConfig.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/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index 040c1f584c525990ec844cba9f01df5b33fa2a37..2855f8c4684cb9afee1bd8a4c2ae82d45ef1ea2b 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -256,4 +256,11 @@ public class LeafConfig { + advancementOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.advancement.only-broadcast-to-affected-player", advancementOnlyBroadcastToAffectedPlayer); + deathMessageOnlyBroadcastToAffectedPlayer = getBoolean("settings.broadcasts.death.only-broadcast-to-affected-player", deathMessageOnlyBroadcastToAffectedPlayer); + } ++ ++ public static boolean forceHalloweenSeason = false; ++ public static float chanceHeadHalloweenOnEntity = 0.25F; ++ private static void halloweenSetting() { ++ forceHalloweenSeason = getBoolean("gameplay-mechanics.halloween.force", forceHalloweenSeason); ++ chanceHeadHalloweenOnEntity = (float) getDouble("gameplay-mechanics.halloween.head-chance", chanceHeadHalloweenOnEntity); ++ } + } diff --git a/patches/server/0019-Purpur-Persistent-BlockEntity-Lore-and-DisplayName.patch b/patches/server/0019-Purpur-Persistent-BlockEntity-Lore-and-DisplayName.patch new file mode 100644 index 00000000..f62cbd51 --- /dev/null +++ b/patches/server/0019-Purpur-Persistent-BlockEntity-Lore-and-DisplayName.patch @@ -0,0 +1,213 @@ +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] Purpur: Persistent BlockEntity Lore and DisplayName + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +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 b0204af850ee182773ad458208cccd946ad148d5..f74e420b1791df528a30a1213bb0076b4d9d545d 100644 +--- a/src/main/java/net/minecraft/world/item/BlockItem.java ++++ b/src/main/java/net/minecraft/world/item/BlockItem.java +@@ -153,7 +153,24 @@ public class BlockItem extends Item { + } + + protected boolean updateCustomBlockEntityTag(BlockPos pos, Level world, @Nullable Player player, ItemStack stack, BlockState state) { +- return BlockItem.updateCustomBlockEntityTag(world, player, pos, stack); ++ // Purpur start ++ boolean handled = updateCustomBlockEntityTag(world, player, pos, stack); ++ if (org.dreeam.leaf.LeafConfig.persistentTileEntityDisplayNames && stack.hasTag()) { ++ CompoundTag display = stack.getTagElement("display"); ++ if (display != null) { ++ BlockEntity blockEntity = world.getBlockEntity(pos); ++ if (blockEntity != null) { ++ if (display.contains("Name", 8)) { ++ blockEntity.setPersistentDisplayName(display.getString("Name")); ++ } ++ if (display.contains("Lore", 9)) { ++ blockEntity.setPersistentLore(display.getList("Lore", 8)); ++ } ++ } ++ } ++ } ++ return handled; ++ // Purpur end + } + + @Nullable +diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java +index 7b71073027f4cf79736546500ededdfbb83d968e..98c9c1899a311e76179f72fa1c43830fb5b9c521 100644 +--- a/src/main/java/net/minecraft/world/level/block/Block.java ++++ b/src/main/java/net/minecraft/world/level/block/Block.java +@@ -63,6 +63,13 @@ import net.minecraft.world.phys.shapes.Shapes; + import net.minecraft.world.phys.shapes.VoxelShape; + import org.slf4j.Logger; + ++// Purpur start ++import net.minecraft.nbt.CompoundTag; ++import net.minecraft.nbt.ListTag; ++import net.minecraft.nbt.StringTag; ++import net.minecraft.world.Nameable; ++// Purpur end ++ + public class Block extends BlockBehaviour implements ItemLike { + + private static final Logger LOGGER = LogUtils.getLogger(); +@@ -325,7 +332,7 @@ public class Block extends BlockBehaviour implements ItemLike { + public static void dropResources(BlockState state, LevelAccessor world, BlockPos pos, @Nullable BlockEntity blockEntity) { + if (world instanceof ServerLevel) { + Block.getDrops(state, (ServerLevel) world, pos, blockEntity).forEach((itemstack) -> { +- Block.popResource((ServerLevel) world, pos, itemstack); ++ Block.popResource((ServerLevel) world, pos, applyDisplayNameAndLoreFromTile(itemstack, blockEntity)); // Purpur + }); + state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true); + } +@@ -341,7 +348,7 @@ public class Block extends BlockBehaviour implements ItemLike { + io.papermc.paper.event.block.BlockBreakBlockEvent event = new io.papermc.paper.event.block.BlockBreakBlockEvent(org.bukkit.craftbukkit.block.CraftBlock.at(world, pos), org.bukkit.craftbukkit.block.CraftBlock.at(world, source), items); + event.callEvent(); + for (var drop : event.getDrops()) { +- popResource(world.getMinecraftWorld(), pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop)); ++ popResource(world.getMinecraftWorld(), pos, applyDisplayNameAndLoreFromTile(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop), blockEntity)); // Purpur + } + state.spawnAfterBreak(world.getMinecraftWorld(), pos, ItemStack.EMPTY, true); + } +@@ -352,13 +359,53 @@ public class Block extends BlockBehaviour implements ItemLike { + public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, Entity entity, ItemStack stack) { + if (world instanceof ServerLevel) { + Block.getDrops(state, (ServerLevel) world, pos, blockEntity, entity, stack).forEach((itemstack1) -> { +- Block.popResource(world, pos, itemstack1); ++ Block.popResource(world, pos, applyDisplayNameAndLoreFromTile(itemstack1, blockEntity)); // Purpur + }); + state.spawnAfterBreak((ServerLevel) world, pos, stack, true); + } + + } + ++ // Purpur start ++ private static ItemStack applyDisplayNameAndLoreFromTile(ItemStack stack, BlockEntity blockEntity) { ++ if (stack.getItem() instanceof BlockItem) { ++ if (blockEntity != null && blockEntity.getLevel() instanceof ServerLevel && org.dreeam.leaf.LeafConfig.persistentTileEntityDisplayNames) { ++ String name = blockEntity.getPersistentDisplayName(); ++ ListTag lore = blockEntity.getPersistentLore(); ++ if (blockEntity instanceof Nameable) { ++ Nameable namedTile = (Nameable) blockEntity; ++ if (namedTile.hasCustomName()) { ++ name = Component.Serializer.toJson(namedTile.getCustomName()); ++ } ++ } ++ ++ if (name != null || lore != null) { ++ CompoundTag display = stack.getTagElement("display"); ++ if (display == null) { ++ display = new CompoundTag(); ++ } ++ ++ if (name != null) { ++ display.put("Name", StringTag.valueOf(name)); ++ } ++ if (lore != null) { ++ display.put("Lore", lore); ++ } ++ ++ CompoundTag tag = stack.getTag(); ++ if (tag == null) { ++ tag = new CompoundTag(); ++ } ++ tag.put("display", display); ++ ++ stack.setTag(tag); ++ } ++ } ++ } ++ return stack; ++ } ++ // Purpur end ++ + public static void popResource(Level world, BlockPos pos, ItemStack stack) { + float f = EntityType.ITEM.getHeight() / 2.0F; + // Paper start - don't convert potentially massive numbers to floats +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 58986bc0677c5ea1ad54d7d6d4efa5c2ea233aea..59d616137088af46d4494171fe96ba0129082496 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java +@@ -6,6 +6,8 @@ import net.minecraft.CrashReportCategory; + import net.minecraft.core.BlockPos; + import net.minecraft.core.registries.BuiltInRegistries; + import net.minecraft.nbt.CompoundTag; ++import net.minecraft.nbt.ListTag; ++import net.minecraft.nbt.StringTag; + import net.minecraft.network.protocol.Packet; + import net.minecraft.network.protocol.game.ClientGamePacketListener; + import net.minecraft.resources.ResourceLocation; +@@ -74,10 +76,27 @@ public abstract class BlockEntity { + if (persistentDataTag instanceof CompoundTag) { + this.persistentDataContainer.putAll((CompoundTag) persistentDataTag); + } ++ // Purpur start ++ if (nbt.contains("Purpur.persistentDisplayName")) { ++ this.persistentDisplayName = nbt.getString("Purpur.persistentDisplayName"); ++ } ++ if (nbt.contains("Purpur.persistentLore")) { ++ this.persistentLore = nbt.getList("Purpur.persistentLore", 8); ++ } ++ // Purpur end + } + // CraftBukkit end + +- protected void saveAdditional(CompoundTag nbt) {} ++ protected void saveAdditional(CompoundTag nbt) { ++ // Purpur start ++ if (this.persistentDisplayName != null) { ++ nbt.put("Purpur.persistentDisplayName", StringTag.valueOf(this.persistentDisplayName)); ++ } ++ if (this.persistentLore != null) { ++ nbt.put("Purpur.persistentLore", this.persistentLore); ++ } ++ // Purpur end ++ } + + public final CompoundTag saveWithFullMetadata() { + CompoundTag nbttagcompound = this.saveWithoutMetadata(); +@@ -264,4 +283,24 @@ public abstract class BlockEntity { + } + // Paper end + ++ // Purpur start ++ private String persistentDisplayName = null; ++ private ListTag persistentLore = null; ++ ++ public void setPersistentDisplayName(String json) { ++ this.persistentDisplayName = json; ++ } ++ ++ public void setPersistentLore(ListTag lore) { ++ this.persistentLore = lore; ++ } ++ ++ public String getPersistentDisplayName() { ++ return this.persistentDisplayName; ++ } ++ ++ public ListTag getPersistentLore() { ++ return this.persistentLore; ++ } ++ // Purpur end + } +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index 2855f8c4684cb9afee1bd8a4c2ae82d45ef1ea2b..f87f4f19738dfe8b58d3dbf0c27933fd3ec93883 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -263,4 +263,9 @@ public class LeafConfig { + forceHalloweenSeason = getBoolean("gameplay-mechanics.halloween.force", forceHalloweenSeason); + chanceHeadHalloweenOnEntity = (float) getDouble("gameplay-mechanics.halloween.head-chance", chanceHeadHalloweenOnEntity); + } ++ ++ public static boolean persistentTileEntityDisplayNames = false; ++ private static void tileentitySetting() { ++ persistentTileEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-tileentity-display-names-and-lore", persistentTileEntityDisplayNames); ++ } + } diff --git a/patches/server/0020-Purpur-Add-more-logger-output-for-invalid-movement-k.patch b/patches/server/0020-Purpur-Add-more-logger-output-for-invalid-movement-k.patch new file mode 100644 index 00000000..df20695d --- /dev/null +++ b/patches/server/0020-Purpur-Add-more-logger-output-for-invalid-movement-k.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Wed, 27 Jul 2022 00:42:39 -0500 +Subject: [PATCH] Purpur: Add more logger output for invalid movement kicks + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 215eda414547bfdf662b4ea511fa3f774e125d94..7e10dca6450ca112db18a0ff03dbd2330a1d884a 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -813,6 +813,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + if (packet.getId() == this.awaitingTeleport) { + if (this.awaitingPositionFromClient == null) { + this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause ++ ServerGamePacketListenerImpl.LOGGER.warn("Disconnected on accept teleport packet. Was not expecting position data from client at this time"); // Purpur + return; + } + +@@ -1374,8 +1375,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + @Override + public void handleMovePlayer(ServerboundMovePlayerPacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); +- if (ServerGamePacketListenerImpl.containsInvalidValues(packet.getX(0.0D), packet.getY(0.0D), packet.getZ(0.0D), packet.getYRot(0.0F), packet.getXRot(0.0F))) { ++ // Purpur start ++ boolean invalidX = Double.isNaN(packet.getX(0.0D)); ++ boolean invalidY = Double.isNaN(packet.getY(0.0D)); ++ boolean invalidZ = Double.isNaN(packet.getZ(0.0D)); ++ boolean invalidYaw = !Floats.isFinite(packet.getYRot(0.0F)); ++ boolean invalidPitch = !Floats.isFinite(packet.getXRot(0.0F)); ++ if (invalidX || invalidY || invalidZ || invalidYaw || invalidPitch) { + this.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause ++ ServerGamePacketListenerImpl.LOGGER.warn(String.format("Disconnected on move player packet. Invalid data: x=%b, y=%b, z=%b, yaw=%b, pitch=%b", invalidX, invalidY, invalidZ, invalidYaw, invalidPitch)); ++ // Purpur end + } else { + ServerLevel worldserver = this.player.getLevel(); + diff --git a/patches/server/0021-Purpur-Log-skipped-entity-s-position.patch b/patches/server/0021-Purpur-Log-skipped-entity-s-position.patch new file mode 100644 index 00000000..949caedd --- /dev/null +++ b/patches/server/0021-Purpur-Log-skipped-entity-s-position.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Thu, 24 Nov 2022 11:00:37 -0600 +Subject: [PATCH] Purpur: Log skipped entity's position + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java +index 4f3708f5f5dcb6af8225bda1cc9204a6d974665f..6bdef01513cf86d6bf578ca37b364eab70f07843 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityType.java ++++ b/src/main/java/net/minecraft/world/entity/EntityType.java +@@ -576,6 +576,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/0022-Purpur-Alternative-Keepalive-Handling.patch b/patches/server/0022-Purpur-Alternative-Keepalive-Handling.patch new file mode 100644 index 00000000..dba3f37b --- /dev/null +++ b/patches/server/0022-Purpur-Alternative-Keepalive-Handling.patch @@ -0,0 +1,73 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Fri, 11 Oct 2019 00:17:39 -0500 +Subject: [PATCH] Purpur: Alternative Keepalive Handling + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 7e10dca6450ca112db18a0ff03dbd2330a1d884a..a3642fd721dbaad280df032007ec4412bc8f484d 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -260,6 +260,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + private long keepAliveTime = Util.getMillis(); + private boolean keepAlivePending; + private long keepAliveChallenge; ++ private it.unimi.dsi.fastutil.longs.LongList keepAlives = new it.unimi.dsi.fastutil.longs.LongArrayList(); // Purpur + // CraftBukkit start - multithreaded fields + private final AtomicInteger chatSpamTickCount = new AtomicInteger(); + private final java.util.concurrent.atomic.AtomicInteger tabSpamLimiter = new java.util.concurrent.atomic.AtomicInteger(); // Paper - configurable tab spam limits +@@ -401,6 +402,21 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + long currentTime = Util.getMillis(); + long elapsedTime = currentTime - this.keepAliveTime; + ++ // Purpur start ++ if (org.dreeam.leaf.LeafConfig.useAlternateKeepAlive) { ++ if (elapsedTime >= 1000L) { // 1 second ++ if (!processedDisconnect && keepAlives.size() * 1000L >= KEEPALIVE_LIMIT) { ++ LOGGER.warn("{} was kicked due to keepalive timeout!", player.getName()); ++ disconnect(Component.translatable("disconnect.timeout"), org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); ++ } else { ++ keepAliveTime = currentTime; // hijack this field for 1 second intervals ++ keepAlives.add(currentTime); // currentTime is ID ++ send(new ClientboundKeepAlivePacket(currentTime)); ++ } ++ } ++ } else ++ // Purpur end ++ + if (this.keepAlivePending) { + if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected + ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); // more info +@@ -3467,6 +3483,16 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + + @Override + public void handleKeepAlive(ServerboundKeepAlivePacket packet) { ++ // Purpur start ++ if (org.dreeam.leaf.LeafConfig.useAlternateKeepAlive) { ++ long id = packet.getId(); ++ if (keepAlives.size() > 0 && keepAlives.contains(id)) { ++ int ping = (int) (Util.getMillis() - id); ++ player.latency = (player.latency * 3 + ping) / 4; ++ keepAlives.clear(); // we got a valid response, lets roll with it and forget the rest ++ } ++ } else ++ // Purpur end + //PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); // CraftBukkit // Paper - This shouldn't be on the main thread + if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) { + int i = (int) (Util.getMillis() - this.keepAliveTime); +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index f87f4f19738dfe8b58d3dbf0c27933fd3ec93883..9403774691cb83a3b916b4e0dc603385aceb4f4d 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -268,4 +268,9 @@ public class LeafConfig { + private static void tileentitySetting() { + persistentTileEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-tileentity-display-names-and-lore", persistentTileEntityDisplayNames); + } ++ ++ public static boolean useAlternateKeepAlive = false; ++ private static void useAlternateKeepAlive() { ++ useAlternateKeepAlive = getBoolean("settings.use-alternate-keepalive", useAlternateKeepAlive); ++ } + } diff --git a/patches/server/0023-Purpur-Skip-events-if-there-s-no-listeners.patch b/patches/server/0023-Purpur-Skip-events-if-there-s-no-listeners.patch new file mode 100644 index 00000000..b0ad8848 --- /dev/null +++ b/patches/server/0023-Purpur-Skip-events-if-there-s-no-listeners.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Sat, 4 Apr 2020 03:07:59 -0500 +Subject: [PATCH] Purpur: Skip events if there's no listeners + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java +index 8ee8ff15f4511684261ff7a9417baa2ccd313b69..31e44f59bd34ae79dd1147263e446e6d6e9f8c66 100644 +--- a/src/main/java/net/minecraft/commands/Commands.java ++++ b/src/main/java/net/minecraft/commands/Commands.java +@@ -429,6 +429,7 @@ public class Commands { + private void runSync(ServerPlayer player, Collection bukkit, RootCommandNode rootcommandnode) { + // Paper end - Async command map building + new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper ++ if (PlayerCommandSendEvent.getHandlerList().getRegisteredListeners().length > 0) { // Purpur - skip all this crap if there's nothing listening + PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit)); + event.getPlayer().getServer().getPluginManager().callEvent(event); + +@@ -439,6 +440,7 @@ public class Commands { + } + } + // CraftBukkit end ++ } // Purpur - skip event + player.connection.send(new ClientboundCommandsPacket(rootcommandnode)); + } + diff --git a/patches/server/0024-Purpur-Add-toggle-for-sand-duping-fix.patch b/patches/server/0024-Purpur-Add-toggle-for-sand-duping-fix.patch new file mode 100644 index 00000000..7c236cae --- /dev/null +++ b/patches/server/0024-Purpur-Add-toggle-for-sand-duping-fix.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Fri, 4 Jun 2021 09:13:54 -0500 +Subject: [PATCH] Purpur: Add toggle for sand duping fix + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +index 72f1866226269396ba0f0c1be269e237925d9322..fe813aea811d3b5c54978d33e7b766f7a9fd17ee 100644 +--- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java ++++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +@@ -130,7 +130,7 @@ public class FallingBlockEntity extends Entity { + @Override + public void tick() { + // Paper start - fix sand duping +- if (this.isRemoved()) { ++ if (org.dreeam.leaf.LeafConfig.fixSandDuping && this.isRemoved()) { // Purpur + return; + } + // Paper end - fix sand duping +@@ -147,7 +147,7 @@ public class FallingBlockEntity extends Entity { + this.move(MoverType.SELF, this.getDeltaMovement()); + + // Paper start - fix sand duping +- if (this.isRemoved()) { ++ if (org.dreeam.leaf.LeafConfig.fixSandDuping && this.isRemoved()) { // Purpur + return; + } + // Paper end - fix sand duping +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index 9403774691cb83a3b916b4e0dc603385aceb4f4d..1a2c32e6589de7705af26623a0c6badb9504324f 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -273,4 +273,9 @@ public class LeafConfig { + private static void useAlternateKeepAlive() { + useAlternateKeepAlive = getBoolean("settings.use-alternate-keepalive", useAlternateKeepAlive); + } ++ ++ public static boolean fixSandDuping = true; ++ private static void sandSettings() { ++ fixSandDuping = getBoolean("blocks.sand.fix-duping", fixSandDuping); ++ } + } diff --git a/patches/server/0025-Purpur-Lobotomize-stuck-villagers.patch b/patches/server/0025-Purpur-Lobotomize-stuck-villagers.patch new file mode 100644 index 00000000..72be9b18 --- /dev/null +++ b/patches/server/0025-Purpur-Lobotomize-stuck-villagers.patch @@ -0,0 +1,129 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath +Date: Thu, 3 Dec 2020 17:56:18 -0600 +Subject: [PATCH] Purpur: Lobotomize stuck villagers + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +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 76a9da8209d557b913c49ccd281bf147b9ac4fa4..a39d038b5198c9244e2225e0fe383f3c96b49a08 100644 +--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java ++++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java +@@ -139,6 +139,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + }, MemoryModuleType.MEETING_POINT, (entityvillager, holder) -> { + return holder.is(PoiTypes.MEETING); + }); ++ private boolean isLobotomized = false; public boolean isLobotomized() { return this.isLobotomized; } // Purpur ++ private int notLobotomizedCount = 0; // Purpur + + public long nextGolemPanic = -1; // Pufferfish + +@@ -155,6 +157,47 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE)); + } + ++ private boolean checkLobotomized() { ++ int interval = org.dreeam.leaf.LeafConfig.villagerLobotomizeCheckInterval; ++ if (this.notLobotomizedCount > 3) { ++ // check half as often if not lobotomized for the last 3+ consecutive checks ++ interval *= 2; ++ } ++ if (this.level.getGameTime() % interval == 0) { ++ // offset Y for short blocks like dirt_path/farmland ++ this.isLobotomized = !canTravelFrom(new BlockPos(getX(), getY() + 0.0625D, getZ())); ++ ++ if (this.isLobotomized) { ++ this.notLobotomizedCount = 0; ++ } else { ++ this.notLobotomizedCount++; ++ } ++ } ++ return this.isLobotomized; ++ } ++ ++ private boolean canTravelFrom(BlockPos pos) { ++ return canTravelTo(pos.east()) || canTravelTo(pos.west()) || canTravelTo(pos.north()) || canTravelTo(pos.south()); ++ } ++ ++ private boolean canTravelTo(BlockPos pos) { ++ net.minecraft.world.level.block.state.BlockState state = this.level.getBlockStateIfLoaded(pos); ++ if (state == null) { ++ // chunk not loaded ++ return false; ++ } ++ net.minecraft.world.level.block.Block bottom = state.getBlock(); ++ if (bottom instanceof net.minecraft.world.level.block.FenceBlock || ++ bottom instanceof net.minecraft.world.level.block.FenceGateBlock || ++ bottom instanceof net.minecraft.world.level.block.WallBlock) { ++ // bottom block is too tall to get over ++ return false; ++ } ++ net.minecraft.world.level.block.Block top = level.getBlockState(pos.above()).getBlock(); ++ // only if both blocks have no collision ++ return !bottom.hasCollision && !top.hasCollision; ++ } ++ + @Override + public Brain getBrain() { + return (Brain) super.getBrain(); // CraftBukkit - decompile error +@@ -250,12 +293,27 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + protected void customServerAiStep() { mobTick(false); } + protected void mobTick(boolean inactive) { + this.level.getProfiler().push("villagerBrain"); ++ // Purpur start ++ if (org.dreeam.leaf.LeafConfig.villagerLobotomizeEnabled) { ++ // treat as inactive if lobotomized ++ inactive = inactive || checkLobotomized(); ++ } else { ++ // clean up state for API ++ this.isLobotomized = false; ++ } ++ // Purpur end + // Pufferfish start + if (!inactive) { + if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel) this.level, this); // Paper + } + // Pufferfish end ++ // Purpur start ++ else if (this.isLobotomized && shouldRestock()) { ++ // make sure we restock if needed when lobotomized ++ restock(); ++ } ++ // Purpur end + this.level.getProfiler().pop(); + if (this.assignProfessionWhenSpawned) { + this.assignProfessionWhenSpawned = false; +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java +index a1a8ac55e572156671e47317ba061855be79e5ac..ec3fb8865211bd7625103c37af7b96df37163a07 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java +@@ -222,4 +222,11 @@ public class CraftVillager extends CraftAbstractVillager implements Villager { + getHandle().getGossips().gossips.clear(); + } + // Paper end ++ ++ // Purpur start ++ @Override ++ public boolean isLobotomized() { ++ return getHandle().isLobotomized(); ++ } ++ // Purpur end + } +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index 1a2c32e6589de7705af26623a0c6badb9504324f..e3a2432c09871878c4c3981019d602a91fec3af7 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -278,4 +278,11 @@ public class LeafConfig { + private static void sandSettings() { + fixSandDuping = getBoolean("blocks.sand.fix-duping", fixSandDuping); + } ++ ++ public static boolean villagerLobotomizeEnabled = false; ++ public static int villagerLobotomizeCheckInterval = 100; ++ private static void villagerSettings() { ++ villagerLobotomizeEnabled = getBoolean("mobs.villager.lobotomize.enabled", villagerLobotomizeEnabled); ++ villagerLobotomizeCheckInterval = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval); ++ } + } diff --git a/patches/server/0026-Purpur-Option-to-disable-kick-for-out-of-order-chat.patch b/patches/server/0026-Purpur-Option-to-disable-kick-for-out-of-order-chat.patch new file mode 100644 index 00000000..ec0f967e --- /dev/null +++ b/patches/server/0026-Purpur-Option-to-disable-kick-for-out-of-order-chat.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Sun, 3 Jul 2022 04:13:57 -0500 +Subject: [PATCH] Purpur: Option to disable kick for out of order chat + +Original license: MIT +Original project: https://github.com/PurpurMC/Purpur + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index a3642fd721dbaad280df032007ec4412bc8f484d..35afbe22475309f165fe8b540b51648ab41ed394 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -2387,7 +2387,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + do { + instant1 = (Instant) this.lastChatTimeStamp.get(); + if (timestamp.isBefore(instant1)) { +- return false; ++ return !org.dreeam.leaf.LeafConfig.kickForOutOfOrderChat; // Purpur + } + } while (!this.lastChatTimeStamp.compareAndSet(instant1, timestamp)); + +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +index e3a2432c09871878c4c3981019d602a91fec3af7..519770e60a1986e00b9754bb375b621cf084ec88 100644 +--- a/src/main/java/org/dreeam/leaf/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -285,4 +285,9 @@ public class LeafConfig { + villagerLobotomizeEnabled = getBoolean("mobs.villager.lobotomize.enabled", villagerLobotomizeEnabled); + villagerLobotomizeCheckInterval = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval); + } ++ ++ public static boolean kickForOutOfOrderChat = true; ++ private static void networkSettings() { ++ kickForOutOfOrderChat = getBoolean("settings.network.kick-for-out-of-order-chat", kickForOutOfOrderChat); ++ } + } diff --git a/patches/server/0027-KeYi-Smarter-statistics-ticking.patch b/patches/server/0027-KeYi-Smarter-statistics-ticking.patch new file mode 100644 index 00000000..74b775a6 --- /dev/null +++ b/patches/server/0027-KeYi-Smarter-statistics-ticking.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mykyta Komarnytskyy +Date: Sat, 24 Oct 2020 21:03:53 -0500 +Subject: [PATCH] KeYi: Smarter statistics ticking + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +In vanilla, statistics that count time spent for an action (i.e. time played or sneak time) are incremented every tick. This is retarded. With this patch and a configured interval of 20, the statistics are only ticked every 20th tick and are incremented by 20 ticks at a time. This means a lot less ticking with the same accurate counting. +With an interval of 20, this patch saves roughly 3ms per tick on a server w/ 80 players online. + +Original code by YatopiaMC, licensed under MIT +You can find the original code on https://github.com/YatopiaMC/Yatopia + +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 2e6557a19523d18aecff709de30cb4466b46a9fa..2ca483dea4eabd5f58076e5676e2ce4d0b1d9045 100644 +--- a/src/main/java/net/minecraft/world/entity/player/Player.java ++++ b/src/main/java/net/minecraft/world/entity/player/Player.java +@@ -277,19 +277,21 @@ public abstract class Player extends LivingEntity { + this.moveCloak(); + if (!this.level.isClientSide) { + this.foodData.tick(this); +- this.awardStat(Stats.PLAY_TIME); +- this.awardStat(Stats.TOTAL_WORLD_TIME); +- if (this.isAlive()) { +- this.awardStat(Stats.TIME_SINCE_DEATH); +- } +- +- if (this.isDiscrete()) { +- this.awardStat(Stats.CROUCH_TIME); +- } +- +- if (!this.isSleeping()) { +- this.awardStat(Stats.TIME_SINCE_REST); ++ // Mirai start ++ if (tickCount % 20 == 0) { ++ this.awardStat(Stats.PLAY_TIME, 20); ++ this.awardStat(Stats.TOTAL_WORLD_TIME, 20); ++ if (this.isAlive()) { ++ this.awardStat(Stats.TIME_SINCE_DEATH, 20); ++ } ++ if (this.isDiscrete()) { ++ this.awardStat(Stats.CROUCH_TIME, 20); ++ } ++ if (!this.isSleeping()) { ++ this.awardStat(Stats.TIME_SINCE_REST, 20); ++ } + } ++ // Mirai end + } + + int i = 29999999; diff --git a/patches/server/0028-KeYi-Fast-speed-check.patch b/patches/server/0028-KeYi-Fast-speed-check.patch new file mode 100644 index 00000000..e67ed5ae --- /dev/null +++ b/patches/server/0028-KeYi-Fast-speed-check.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: nostalgic853 +Date: Sat, 22 Oct 2022 09:58:38 +0800 +Subject: [PATCH] KeYi: Fast speed check + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +Original code by NFT-Worlds, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/NFT-Worlds/Server + +This patch was took from Mirai. (https://github.com/etil2jz/Mirai) + +etil2jz's note: +NFT-Worlds is related to Stellar devs, known for countless paid forks mostly taking open source patches, +doing questionable/buggy ones, and claiming breathtaking performance improvements. Never ever pay for +any of those Spigot forks! + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 37f1e4aa89da81fcae2d38bb741f3386950bdf51..0dec640d663defdd84b5a22a2668a20fde62d95c 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -1193,7 +1193,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + } + + this.tryCheckInsideBlocks(); +- float f2 = this.getBlockSpeedFactor(); ++ // KeYi start - Fast speed check ++ float f2; ++ if (this.getDeltaMovement().x == 0 && this.getDeltaMovement().z == 0) { ++ f2 = 1; ++ } else { ++ f2 = this.getBlockSpeedFactor(); ++ } ++ // KeYi end + + this.setDeltaMovement(this.getDeltaMovement().multiply((double) f2, 1.0D, (double) f2)); + // Paper start - remove expensive streams from here diff --git a/patches/server/0029-KeYi-Do-not-save-firework-entities-or-the-server-may.patch b/patches/server/0029-KeYi-Do-not-save-firework-entities-or-the-server-may.patch new file mode 100644 index 00000000..f612fe7a --- /dev/null +++ b/patches/server/0029-KeYi-Do-not-save-firework-entities-or-the-server-may.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: nostalgic853 +Date: Mon, 24 Oct 2022 10:25:40 +0800 +Subject: [PATCH] KeYi: Do not save firework entities or the server may be + laggy + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +diff --git a/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java b/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java +index 5406925cd66f46ab8744123c670d72cea7bfc3a1..0b664dfef68b1e3905c9d8451602abf9cd9eafe6 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java +@@ -358,4 +358,11 @@ public class FireworkRocketEntity extends Projectile implements ItemSupplier { + public boolean isAttackable() { + return false; + } ++ ++ // KeYi start ++ @Override ++ public boolean shouldBeSaved() { ++ return false; ++ } ++ // KeYi end + } diff --git a/patches/server/0030-KeYi-Don-t-create-new-random-instance.patch b/patches/server/0030-KeYi-Don-t-create-new-random-instance.patch new file mode 100644 index 00000000..afa24b94 --- /dev/null +++ b/patches/server/0030-KeYi-Don-t-create-new-random-instance.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: foss-mc <69294560+foss-mc@users.noreply.github.com> +Date: Thu, 1 Jul 2021 12:17:44 +0000 +Subject: [PATCH] KeYi: Don't create new random instance + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +Original code by PatinaMC, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/PatinaMC/Patina + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 0ff21883cff37d1df7fda9b2cb93abcfd43f6482..cabed5594c2fb611d92d6319fe83259b8ca5bc21 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -395,7 +395,7 @@ public class ServerPlayer extends Player { + long l = k * k; + int i1 = l > 2147483647L ? Integer.MAX_VALUE : (int) l; + int j1 = this.getCoprime(i1); +- int k1 = RandomSource.create().nextInt(i1); ++ int k1 = worldserver.random.nextInt(i1); // Patina - don't create new random instance + + for (int l1 = 0; l1 < i1; ++l1) { + int i2 = (k1 + j1 * l1) % i1; +@@ -432,7 +432,7 @@ public class ServerPlayer extends Player { + long l = k * k; + int i1 = l > 2147483647L ? Integer.MAX_VALUE : (int) l; + int j1 = this.getCoprime(i1); +- int k1 = RandomSource.create().nextInt(i1); ++ int k1 = world.random.nextInt(i1); // Patina - don't create new random instance + + for (int l1 = 0; l1 < i1; ++l1) { + int i2 = (k1 + j1 * l1) % i1; +diff --git a/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java b/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java +index 1ef089dbf83de35d875c00efdf468c397be56978..c345f10cbf7f3451edc604f97cdf959d70639e17 100644 +--- a/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java ++++ b/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java +@@ -349,7 +349,7 @@ public class QueryThreadGs4 extends GenericThread { + this.identBytes[2] = bs[5]; + this.identBytes[3] = bs[6]; + this.ident = new String(this.identBytes, StandardCharsets.UTF_8); +- this.challenge = RandomSource.create().nextInt(16777216); ++ this.challenge = java.util.concurrent.ThreadLocalRandom.current().nextInt(16777216); // Patina - don't create new random instance + this.challengeBytes = String.format(Locale.ROOT, "\t%s%d\u0000", this.ident, this.challenge).getBytes(StandardCharsets.UTF_8); + } + diff --git a/patches/server/0031-KeYi-Skip-entity-move-if-no-movement.patch b/patches/server/0031-KeYi-Skip-entity-move-if-no-movement.patch new file mode 100644 index 00000000..186555c0 --- /dev/null +++ b/patches/server/0031-KeYi-Skip-entity-move-if-no-movement.patch @@ -0,0 +1,47 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: nostalgic853 +Date: Mon, 24 Oct 2022 11:01:36 +0800 +Subject: [PATCH] KeYi: Skip entity move if no movement + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +Copyright (c) 2021-2022 ishland + +Original code by RelativityMC, licensed under MIT +You can find the original code on https://github.com/RelativityMC/VMP-fabric (Yarn mappings) + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 0dec640d663defdd84b5a22a2668a20fde62d95c..a88b54d2dbae6951d475ca11eddd444e25c178ad 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -397,6 +397,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + @javax.annotation.Nullable + private UUID originWorld; + public boolean freezeLocked = false; // Paper - Freeze Tick Lock API ++ private boolean boundingBoxChanged = false; // KeYi + + public void setOrigin(@javax.annotation.Nonnull Location location) { + this.origin = location.toVector(); +@@ -1033,6 +1034,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + // Paper end - detailed watchdog information + + public void move(MoverType movementType, Vec3 movement) { ++ // KeYi start ++ if (!boundingBoxChanged && movement.equals(Vec3.ZERO)) { ++ boundingBoxChanged = false; ++ return; ++ } ++ // KeYi end ++ + // Paper start - detailed watchdog information + io.papermc.paper.util.TickThread.ensureTickThread("Cannot move an entity off-main"); + synchronized (this.posLock) { +@@ -3814,6 +3822,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + } + + public final void setBoundingBox(AABB boundingBox) { ++ if (!this.bb.equals(boundingBox)) boundingBoxChanged = true; // KeYi + // CraftBukkit start - block invalid bounding boxes + double minX = boundingBox.minX, + minY = boundingBox.minY, diff --git a/patches/server/0032-KeYi-Optimized-getBiome-method.patch b/patches/server/0032-KeYi-Optimized-getBiome-method.patch new file mode 100644 index 00000000..bc544f00 --- /dev/null +++ b/patches/server/0032-KeYi-Optimized-getBiome-method.patch @@ -0,0 +1,126 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: nostalgic853 +Date: Tue, 25 Oct 2022 00:28:35 +0800 +Subject: [PATCH] KeYi: Optimized getBiome method + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +Original license: MIT +Original project: https://github.com/fxmorin/carpet-fixes + +Optimized the getBiome call to be 25% - 75% faster +This is a fully vanilla optimization. + +diff --git a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java +index 5695c5116c8a338b2e41aafcb2dc9f2146856970..aa65785ac89a876111e5f661e0b1db6650010475 100644 +--- a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java ++++ b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java +@@ -14,6 +14,7 @@ public class BiomeManager { + private static final int ZOOM_MASK = 3; + private final BiomeManager.NoiseBiomeSource noiseBiomeSource; + private final long biomeZoomSeed; ++ private static final double maxOffset = 0.4500000001D; // KeYi + + public BiomeManager(BiomeManager.NoiseBiomeSource storage, long seed) { + this.noiseBiomeSource = storage; +@@ -29,39 +30,68 @@ public class BiomeManager { + } + + public Holder getBiome(BlockPos pos) { +- int i = pos.getX() - 2; +- int j = pos.getY() - 2; +- int k = pos.getZ() - 2; +- int l = i >> 2; +- int m = j >> 2; +- int n = k >> 2; +- double d = (double)(i & 3) / 4.0D; +- double e = (double)(j & 3) / 4.0D; +- double f = (double)(k & 3) / 4.0D; +- int o = 0; +- double g = Double.POSITIVE_INFINITY; +- +- for(int p = 0; p < 8; ++p) { +- boolean bl = (p & 4) == 0; +- boolean bl2 = (p & 2) == 0; +- boolean bl3 = (p & 1) == 0; +- int q = bl ? l : l + 1; +- int r = bl2 ? m : m + 1; +- int s = bl3 ? n : n + 1; +- double h = bl ? d : d - 1.0D; +- double t = bl2 ? e : e - 1.0D; +- double u = bl3 ? f : f - 1.0D; +- double v = getFiddledDistance(this.biomeZoomSeed, q, r, s, h, t, u); +- if (g > v) { +- o = p; +- g = v; ++ // KeYi ++ int xMinus2 = pos.getX() - 2; ++ int yMinus2 = pos.getY() - 2; ++ int zMinus2 = pos.getZ() - 2; ++ int x = xMinus2 >> 2; // BlockPos to BiomePos ++ int y = yMinus2 >> 2; ++ int z = zMinus2 >> 2; ++ double quartX = (double) (xMinus2 & 3) / 4.0D; // quartLocal divided by 4 ++ double quartY = (double) (yMinus2 & 3) / 4.0D; // 0/4, 1/4, 2/4, 3/4 ++ double quartZ = (double) (zMinus2 & 3) / 4.0D; // [0, 0.25, 0.5, 0.75] ++ int smallestX = 0; ++ double smallestDist = Double.POSITIVE_INFINITY; ++ for (int biomeX = 0; biomeX < 8; ++biomeX) { ++ boolean everyOtherQuad = (biomeX & 4) == 0; // 1 1 1 1 0 0 0 0 ++ boolean everyOtherPair = (biomeX & 2) == 0; // 1 1 0 0 1 1 0 0 ++ boolean everyOther = (biomeX & 1) == 0; // 1 0 1 0 1 0 1 0 ++ double quartXX = everyOtherQuad ? quartX : quartX - 1.0D; //[-1.0,-0.75,-0.5,-0.25,0.0,0.25,0.5,0.75] ++ double quartYY = everyOtherPair ? quartY : quartY - 1.0D; ++ double quartZZ = everyOther ? quartZ : quartZ - 1.0D; ++ ++ //This code block is new ++ double maxQuartYY = 0.0D, maxQuartZZ = 0.0D; ++ if (biomeX != 0) { ++ maxQuartYY = Mth.square(Math.max(quartYY + maxOffset, Math.abs(quartYY - maxOffset))); ++ maxQuartZZ = Mth.square(Math.max(quartZZ + maxOffset, Math.abs(quartZZ - maxOffset))); ++ double maxQuartXX = Mth.square(Math.max(quartXX + maxOffset, Math.abs(quartXX - maxOffset))); ++ if (smallestDist < maxQuartXX + maxQuartYY + maxQuartZZ) continue; + } +- } + +- int w = (o & 4) == 0 ? l : l + 1; +- int x = (o & 2) == 0 ? m : m + 1; +- int y = (o & 1) == 0 ? n : n + 1; +- return this.noiseBiomeSource.getNoiseBiome(w, x, y); ++ int xx = everyOtherQuad ? x : x + 1; ++ int yy = everyOtherPair ? y : y + 1; ++ int zz = everyOther ? z : z + 1; ++ ++ //I transferred the code from method_38106 to here, so I could call continue halfway through ++ long seed = LinearCongruentialGenerator.next(this.biomeZoomSeed, xx); ++ seed = LinearCongruentialGenerator.next(seed, yy); ++ seed = LinearCongruentialGenerator.next(seed, zz); ++ seed = LinearCongruentialGenerator.next(seed, xx); ++ seed = LinearCongruentialGenerator.next(seed, yy); ++ seed = LinearCongruentialGenerator.next(seed, zz); ++ double offsetX = getFiddle(seed); ++ double sqrX = Mth.square(quartXX + offsetX); ++ if (biomeX != 0 && smallestDist < sqrX + maxQuartYY + maxQuartZZ) continue; //skip the rest of the loop ++ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); ++ double offsetY = getFiddle(seed); ++ double sqrY = Mth.square(quartYY + offsetY); ++ if (biomeX != 0 && smallestDist < sqrX + sqrY + maxQuartZZ) continue; // skip the rest of the loop ++ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); ++ double offsetZ = getFiddle(seed); ++ double biomeDist = sqrX + sqrY + Mth.square(quartZZ + offsetZ); ++ ++ if (smallestDist > biomeDist) { ++ smallestX = biomeX; ++ smallestDist = biomeDist; ++ } ++ } ++ return this.noiseBiomeSource.getNoiseBiome( ++ (smallestX & 4) == 0 ? x : x + 1, ++ (smallestX & 2) == 0 ? y : y + 1, ++ (smallestX & 1) == 0 ? z : z + 1 ++ ); ++ // KeYi end + } + + public Holder getNoiseBiomeAtPosition(double x, double y, double z) { diff --git a/patches/server/0033-KeYi-Use-optimized-RecipeManager.patch b/patches/server/0033-KeYi-Use-optimized-RecipeManager.patch new file mode 100644 index 00000000..2c8f7f65 --- /dev/null +++ b/patches/server/0033-KeYi-Use-optimized-RecipeManager.patch @@ -0,0 +1,87 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: nostalgic853 +Date: Tue, 25 Oct 2022 00:57:45 +0800 +Subject: [PATCH] KeYi: Use optimized RecipeManager + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +Original license: MIT +Original project: https://github.com/fxmorin/carpet-fixes + +Optimized the RecipeManager getFirstMatch call to be up to 3x faster +This is a fully vanilla optimization. Improves: [Blast]Furnace/Campfire/Smoker/Stonecutter/Crafting/Sheep Color Choosing +This was mostly made for the auto crafting table, since the performance boost is much more visible while using that mod + +diff --git a/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java b/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java +index 2f712bfc1f717ba410bf34669d7b0a919ca218cc..fa7f4c3e14aa4ec11187f7c0f96cb4504ee38547 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java ++++ b/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java +@@ -11,14 +11,7 @@ import com.google.gson.JsonParseException; + import com.google.gson.JsonSyntaxException; + import com.mojang.datafixers.util.Pair; + import com.mojang.logging.LogUtils; +-import java.util.Collection; +-import java.util.Collections; +-import java.util.Comparator; +-import java.util.Iterator; +-import java.util.List; +-import java.util.Map; + import java.util.Map.Entry; +-import java.util.Optional; + import java.util.stream.Collectors; + import java.util.stream.Stream; + import javax.annotation.Nullable; +@@ -33,6 +26,7 @@ import net.minecraft.world.Container; + import net.minecraft.world.item.ItemStack; + import net.minecraft.world.level.Level; + import org.slf4j.Logger; ++import java.util.*; // KeYi + + import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; // CraftBukkit + +@@ -103,13 +97,28 @@ public class RecipeManager extends SimpleJsonResourceReloadListener { + } + + public > Optional getRecipeFor(RecipeType type, C inventory, Level world) { +- // CraftBukkit start +- Optional recipe = this.byType(type).values().stream().filter((irecipe) -> { +- return irecipe.matches(inventory, world); +- }).findFirst(); +- inventory.setCurrentRecipe(recipe.orElse(null)); // CraftBukkit - Clear recipe when no recipe is found +- // CraftBukkit end +- return recipe; ++ // KeYi start ++ int slots = 0; ++ int count; ++ //compare size to quickly remove recipes that are not even close. Plus remove streams ++ for (int slot = 0; slot < inventory.getContainerSize(); slot++) ++ if (!inventory.getItem(slot).isEmpty()) slots++; ++ for (Recipe recipe : this.byType(type).values()) { ++ count = 0; ++ if (recipe instanceof CustomRecipe) { ++ if (recipe.matches(inventory, world)) { ++ return (Optional) Optional.of(recipe); ++ } ++ } else { ++ for (Ingredient ingredient : recipe.getIngredients()) ++ if (ingredient != Ingredient.EMPTY) count++; ++ if (count == slots && recipe.matches(inventory, world)) { ++ return (Optional) Optional.of(recipe); ++ } ++ } ++ } ++ return Optional.empty(); ++ // KeYi end + } + + public > Optional> getRecipeFor(RecipeType type, C inventory, Level world, @Nullable ResourceLocation id) { +@@ -131,7 +140,7 @@ public class RecipeManager extends SimpleJsonResourceReloadListener { + } + + public > List getAllRecipesFor(RecipeType type) { +- return List.copyOf(this.byType(type).values()); ++ return new ArrayList<>(this.byType(type).values()); // KeYi + } + + public > List getRecipesFor(RecipeType type, C inventory, Level world) { diff --git a/patches/server/0034-KeYi-Use-a-faster-random-implementation.patch b/patches/server/0034-KeYi-Use-a-faster-random-implementation.patch new file mode 100644 index 00000000..d03a7833 --- /dev/null +++ b/patches/server/0034-KeYi-Use-a-faster-random-implementation.patch @@ -0,0 +1,530 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: nostalgic853 +Date: Fri, 18 Nov 2022 23:39:13 +0800 +Subject: [PATCH] KeYi: Use a faster random implementation + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +diff --git a/src/main/java/cc/keyimc/keyi/utils/FastRandom.java b/src/main/java/cc/keyimc/keyi/utils/FastRandom.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a9b3a467e7dbfdc78c11f04e6bb5a9642c20470c +--- /dev/null ++++ b/src/main/java/cc/keyimc/keyi/utils/FastRandom.java +@@ -0,0 +1,395 @@ ++/** ++ * This is a faster implementation of java.util.Random. ++ * Code from https://gist.github.com/Xyene/4637619 ++ * Licensed under GNU Lesser General Public License v3.0 ++ *

++ * A random number generator based on the simple and fast xor-shift pseudo ++ * random number generator (RNG) specified in: ++ * Marsaglia, George. (2003). Xorshift RNGs. ++ * http://www.jstatsoft.org/v08/i14/xorshift.pdf ++ * Translated from: ++ * http://www.codeproject.com/Articles/9187/A-fast-equivalent-for-System-Random. ++ */ ++ ++package cc.keyimc.keyi.utils; ++ ++@SuppressWarnings("SuspiciousNameCombination") ++public class FastRandom extends java.util.Random { ++ final double REAL_UNIT_INT = 1.0 / (0x7FFFFFFFL); ++ final double REAL_UNIT_UINT = 1.0 / (0xFFFFFFFFL); ++ final long Y = 842502087L, Z = 3579807591L, W = 273326509L; ++ long x, y, z, w; ++ ++ public FastRandom() { ++ seed((int) System.currentTimeMillis()); ++ } ++ ++ @Override ++ public void setSeed(long seed) { ++ seed((int) seed); ++ } ++ ++ public void seed(int seed) { ++ // The only stipulation stated for the xorshift RNG is that at least one of ++ // the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing ++ // resetting of the x seed ++ x = seed; ++ y = Y; ++ z = Z; ++ w = W; ++ } ++ ++ long boolBuffer; ++ int boolBufferBits = 0; ++ ++ @Override ++ public boolean nextBoolean() { ++ if (boolBufferBits == 0) { ++ boolBuffer = nextUInt(); ++ boolBufferBits = 32; ++ } ++ boolBuffer >>= 1; ++ boolean bit = (boolBuffer & 1) == 0; ++ --boolBufferBits; ++ return bit; ++ } ++ ++ @Override ++ public void nextBytes(byte[] buffer) { ++ // Fill up the bulk of the buffer in chunks of 4 bytes at a time. ++ long x = this.x, y = this.y, z = this.z, w = this.w; ++ int i = 0; ++ long t; ++ for (int bound = buffer.length - 3; i < bound; ) { ++ // Generate 4 bytes. ++ // Increased performance is achieved by generating 4 random bytes per loop. ++ // Also note that no mask needs to be applied to zero out the higher order bytes before ++ // casting because the cast ignores thos bytes. Thanks to Stefan Trosch黷z for pointing this out. ++ t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); ++ ++ buffer[i++] = (byte) w; ++ buffer[i++] = (byte) (w >> 8); ++ buffer[i++] = (byte) (w >> 16); ++ buffer[i++] = (byte) (w >> 24); ++ } ++ ++ // Fill up any remaining bytes in the buffer. ++ if (i < buffer.length) { ++ // Generate 4 bytes. ++ t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); ++ ++ buffer[i++] = (byte) w; ++ if (i < buffer.length) { ++ buffer[i++] = (byte) (w >> 8); ++ if (i < buffer.length) { ++ buffer[i++] = (byte) (w >> 16); ++ if (i < buffer.length) { ++ buffer[i] = (byte) (w >> 24); ++ } ++ } ++ } ++ } ++ this.x = x; ++ this.y = y; ++ this.z = z; ++ this.w = w; ++ } ++ ++ @Override ++ public double nextDouble() { ++ long t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ ++ // Here we can gain a 2x speed improvement by generating a value that can be cast to ++ // an int instead of the more easily available uint. If we then explicitly cast to an ++ // int the compiler will then cast the int to a double to perform the multiplication, ++ // this final cast is a lot faster than casting from a uint to a double. The extra cast ++ // to an int is very fast (the allocated bits remain the same) and so the overall effect ++ // of the extra cast is a significant performance improvement. ++ // ++ // Also note that the loss of one bit of precision is equivalent to what occurs within ++ // System.Random. ++ return (REAL_UNIT_INT * (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))); ++ } ++ ++ public double random() { ++ return nextDouble(); ++ } ++ ++ @Override ++ public float nextFloat() { ++ return (float) nextDouble(); ++ } ++ ++ @Override ++ public int nextInt() { ++ long t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ return (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))); ++ } ++ ++ @Override ++ public int nextInt(int upperBound) { ++ if (upperBound < 0) ++ throw new IllegalArgumentException("upperBound must be >=0"); ++ ++ long t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ ++ return (int) ((REAL_UNIT_INT * (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound); ++ } ++ ++ public int nextInt(int lowerBound, int upperBound) { ++ if (lowerBound > upperBound) ++ throw new IllegalArgumentException("upperBound must be >=lowerBound"); ++ ++ long t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ ++ // The explicit int cast before the first multiplication gives better performance. ++ // See comments in NextDouble. ++ int range = upperBound - lowerBound; ++ if (range < 0) { ++ // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower). ++ // We also must use all 32 bits of precision, instead of the normal 31, which again is slower. ++ return lowerBound + (int) ((REAL_UNIT_UINT * (double) (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (double) ((long) upperBound - (long) lowerBound)); ++ } ++ // 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain ++ // a little more performance. ++ return lowerBound + (int) ((REAL_UNIT_INT * (double) (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * (double) range); ++ } ++ ++ public long nextUInt() { ++ long t = (x ^ (x << 11)); ++ x = y; ++ y = z; ++ z = w; ++ return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))) & (0xFFFFFFFFL); ++ } ++ ++ @Override ++ public long nextLong() { ++ return nextUInt() << 32 + nextUInt(); ++ } ++ ++ double gaussNext; ++ boolean hasGaussNext; ++ final double TWOPI = Math.PI * 2; ++ ++ /** ++ * Get a random number in the range [min, max) or [min, max] depending on rounding. ++ * ++ * @param min Low bound ++ * @param max High bound ++ * @return A uniformly distributed double ++ */ ++ public double uniform(double min, double max) { ++ return min + (max - min) * nextDouble(); ++ } ++ ++ /** ++ * Triangular distribution. ++ *

++ * Continuous distribution bounded by given lower and upper limits, ++ * and having a given mode value in-between. ++ * http://en.wikipedia.org/wiki/Triangular_distribution ++ * ++ * @param low Low bound ++ * @param high High bound ++ * @param mode Mode ++ * @return A number from the triangular distribution specified ++ */ ++ public double triangular(int low, int high, int mode) { ++ double u = nextDouble(); ++ double c = (mode - low) / (high - low); ++ if (u > c) { ++ u = 1.0 - u; ++ c = 1.0 - c; ++ int k = low; ++ low = high; ++ high = k; ++ } ++ return low + (high - low) * Math.sqrt(u * c); ++ } ++ ++ /** ++ * Gaussian distribution, mean is 0 and standard deviation is 1. ++ *

++ * mu is the mean, and sigma is the standard deviation. ++ * ++ * @return A double in Gaussian distribution ++ */ ++ public double gauss() { ++ return nextGaussian(); ++ } ++ ++ /** ++ * Gaussian distribution, with user-specified mean and standard deviation. ++ *

++ * mu is the mean, and sigma is the standard deviation. ++ * ++ * @return A double in Gaussian distribution ++ */ ++ public double gauss(double mu, double sigma) { ++ return mu + sigma * nextGaussian(); ++ } ++ ++ public double gaussUnsigned(double mu, double sigma) { ++ double out = gauss(mu, sigma); ++ return out > 1 ? out : 1; ++ } ++ ++ /** ++ * Log normal distribution. ++ *

++ * If you take the natural logarithm of this distribution, you'll get a ++ * normal distribution with mean mu and standard deviation sigma. ++ * mu can have any value, and sigma must be greater than zero. ++ * ++ * @param mu Mean ++ * @param sigma Standard deviation ++ * @return A number from the log normal distribution specified ++ */ ++ public double logNormal(double mu, double sigma) { ++ return Math.exp(gauss(mu, sigma)); ++ } ++ ++ /** ++ * Exponential distribution. ++ *

++ * lambda is 1.0 divided by the desired mean. It should be ++ * nonzero. Returned values range from 0 to positive infinity ++ * if lambda is positive, and from negative infinity to 0 ++ * if lambda is negative. ++ * ++ * @param lambda A non-zero value ++ */ ++ public double exponential(double lambda) { ++ return -Math.log(1.0 - random()) / lambda; ++ } ++ ++ /** ++ * Circular data distribution. ++ *

++ * If kappa is equal to zero, this distribution reduces ++ * to a uniform random angle over the range 0 to 2*pi. ++ * ++ * @param mu the mean angle, expressed in radians between 0 and 2*pi. ++ * @param kappa the concentration parameter, which must be greater than or ++ * equal to zero. ++ * @return A number from the circular data distribution specified ++ */ ++ public double circularData(double mu, double kappa) { ++ if (kappa <= 1e-6) ++ return TWOPI * nextDouble(); ++ ++ double a = 1.0 + Math.sqrt(1.0 + 4.0 * kappa * kappa); ++ double b = (a - Math.sqrt(2.0 * a)) / (2.0 * kappa); ++ double r = (1.0 + b * b) / (2.0 * b); ++ double u1, u2, u3, f, c, z, theta = 0; ++ ++ while (true) { ++ u1 = nextDouble(); ++ ++ z = Math.cos(Math.PI * u1); ++ f = (1.0 + r * z) / (r + z); ++ c = kappa * (r - f); ++ ++ u2 = nextDouble(); ++ ++ if (u2 < c * (2.0 - c) || u2 <= c * Math.exp(1.0 - c)) ++ break; ++ ++ u3 = nextDouble(); ++ if (u3 > 0.5) ++ theta = (mu % TWOPI) + Math.acos(f); ++ else ++ theta = (mu % TWOPI) - Math.acos(f); ++ } ++ return theta; ++ } ++ ++ ++ final double LOG4 = Math.log(4); ++ final double SG_MAGICCONST = 1.0 + Math.log(4.5); ++ ++ /** ++ * Gamma distribution. Not the gamma function! ++ * Conditions on the parameters are alpha > 0 and beta > 0. ++ *

++ * The probability distribution function is: ++ * pdf(x) = (x ** (alpha - 1) * math.exp(-x / beta)) / (math.gamma(alpha) * beta ** alpha) ++ * ++ * @param alpha Alpha ++ * @param beta Beta ++ * @return A number from the gamma distribution specified ++ */ ++ public double gamma(double alpha, double beta) { ++ if (alpha <= 0.0 || beta <= 0.0) ++ throw new IllegalArgumentException("alpha and beta must be > 0.0"); ++ ++ if (alpha > 1.0) { ++ double ainv = Math.sqrt(2.0 * alpha - 1.0); ++ double bbb = alpha - LOG4; ++ double ccc = alpha + ainv; ++ double u1, u2, v, x, z, r; ++ ++ while (true) { ++ u1 = random(); ++ if (!(1e-7 < u1 && u1 < .9999999)) ++ continue; ++ u2 = 1.0 - random(); ++ v = Math.log(u1 / (1.0 - u1)) / ainv; ++ x = alpha * Math.exp(v); ++ z = u1 * u1 * u2; ++ r = bbb + ccc * v - x; ++ if (r + SG_MAGICCONST - 4.5 * z >= 0.0 || r >= Math.log(z)) ++ return x * beta; ++ } ++ } else if (alpha == 1.0) { ++ // exponential(1) ++ double u; ++ u = random(); ++ while (u <= 1e-7) ++ u = random(); ++ return -Math.log(u) * beta; ++ } else { ++ // alpha is between 0 and 1 (exclusive) ++ // Uses ALGORITHM GS of Statistical Computing -Kennedy & Gentle ++ ++ double u, b, p, x, u1; ++ while (true) { ++ u = random(); ++ b = (Math.E + alpha) / Math.E; ++ p = b * u; ++ if (p <= 1.0) ++ x = Math.pow(p, (1.0 / alpha)); ++ else ++ x = -Math.log((b - p) / alpha); ++ u1 = random(); ++ if (p > 1.0) { ++ if (u1 <= Math.pow(x, (alpha - 1.0))) ++ break; ++ } else if (u1 <= Math.exp(-x)) ++ break; ++ } ++ return x * beta; ++ } ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java +index e5ea9f27a1936ed9e329e74317c91c5df89b9fbd..89a41d396162a1c2eb2df5192b0d888b08fec6ec 100644 +--- a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java ++++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java +@@ -10,10 +10,11 @@ import java.util.HashMap; + import java.util.Map; + import java.util.Random; + import java.util.UUID; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + public class PaperLootableInventoryData { + +- private static final Random RANDOM = new Random(); ++ private static final Random RANDOM = new FastRandom(); // KeYi - use a faster random + + private long lastFill = -1; + private long nextRefill = -1; +diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +index de7a5f3812a017131fd1b32fbeff10e325b1cd2e..ebe2c0b17fc6e21a5ced4f35b594d466f9f573f5 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -53,6 +53,10 @@ import net.minecraft.world.level.material.Fluids; + import net.minecraft.world.ticks.LevelChunkTicks; + import net.minecraft.world.ticks.TickContainerAccess; + import org.slf4j.Logger; ++// KeYi start ++import cc.keyimc.keyi.utils.FastRandom; ++import java.util.Random; // KeYi ++// KeYi end + + public class LevelChunk extends ChunkAccess { + +@@ -932,7 +936,7 @@ public class LevelChunk extends ChunkAccess { + if (this.needsDecoration) { + try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper + this.needsDecoration = false; +- java.util.Random random = new java.util.Random(); ++ Random random = new FastRandom(); + random.setSeed(this.level.getSeed()); + long xRand = random.nextLong() / 2L * 2L + 1L; + long zRand = random.nextLong() / 2L * 2L + 1L; +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java +index dcfe090c269d4cbcc2eb1b6f85392848bb34656c..ef8909c9c13c8f46ec6d452f0d14c9a1119e0edf 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java +@@ -26,6 +26,7 @@ import net.minecraft.nbt.CompoundTag; + import net.minecraft.nbt.NbtIo; + import net.minecraft.world.level.ChunkPos; + import org.slf4j.Logger; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + public class RegionFile implements AutoCloseable { + +@@ -109,7 +110,7 @@ public class RegionFile implements AutoCloseable { + } + + private void backupRegionFile() { +- Path backup = this.regionFile.getParent().resolve(this.regionFile.getFileName() + "." + new java.util.Random().nextLong() + ".backup"); ++ Path backup = this.regionFile.getParent().resolve(this.regionFile.getFileName() + "." + new FastRandom().nextLong() + ".backup"); // KeYi - use a faster random + this.backupRegionFile(backup); + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index f8d321e925bf2708e51590542325c1bdc67d5964..ace7e9f66c3583fc51f06bd9963ed55968d40c0f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -134,6 +134,7 @@ import org.bukkit.util.Consumer; + import org.bukkit.util.RayTraceResult; + import org.bukkit.util.StructureSearchResult; + import org.bukkit.util.Vector; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + public class CraftWorld extends CraftRegionAccessor implements World { + public static final int CUSTOM_DIMENSION_OFFSET = 10; +@@ -226,7 +227,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + } + // Paper end + +- private static final Random rand = new Random(); ++ private static final Random rand = new FastRandom(); // KeYi - use a faster random + + public CraftWorld(ServerLevel world, ChunkGenerator gen, BiomeProvider biomeProvider, Environment env) { + this.world = world; +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java +index d1c7ab67cba881d96b7a5e9220130d86d0514304..309d1ce15aace9da4a84819e8511af2cc12b1ea8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java +@@ -12,10 +12,11 @@ import org.bukkit.entity.EntityType; + import org.bukkit.entity.Firework; + import org.bukkit.entity.LivingEntity; + import org.bukkit.inventory.meta.FireworkMeta; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + public class CraftFirework extends CraftProjectile implements Firework { + +- private final Random random = new Random(); ++ private final Random random = new FastRandom(); // KeYi - use a faster random + //private CraftItemStack item; // Paper - Remove usage, not accurate representation of current item. + + public CraftFirework(CraftServer server, FireworkRocketEntity entity) { +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +index a9673a804d597599c35c83f4f245510c83005328..08d47eee550e2e9ddfb4dc0ce12945401b16c23c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +@@ -42,13 +42,14 @@ import org.bukkit.craftbukkit.util.RandomSourceWrapper; + import org.bukkit.generator.ChunkGenerator; + import org.bukkit.generator.ChunkGenerator.BiomeGrid; + import org.bukkit.generator.ChunkGenerator.ChunkData; ++import cc.keyimc.keyi.utils.FastRandom; // KeYi + + public class CustomChunkGenerator extends InternalChunkGenerator { + + private final net.minecraft.world.level.chunk.ChunkGenerator delegate; + private final ChunkGenerator generator; + private final ServerLevel world; +- private final Random random = new Random(); ++ private final Random random = new FastRandom(); // KeYi - use a faster random + private boolean newApi; + private boolean implementBaseHeight = true; + diff --git a/patches/server/0035-KeYi-Player-Skull-API.patch b/patches/server/0035-KeYi-Player-Skull-API.patch new file mode 100644 index 00000000..6604be98 --- /dev/null +++ b/patches/server/0035-KeYi-Player-Skull-API.patch @@ -0,0 +1,59 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: nostalgic853 +Date: Sun, 20 Nov 2022 00:20:00 +0800 +Subject: [PATCH] KeYi: Player Skull API + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 99bf1485ddecb5fc88850242e9c32850781c4b36..0813237d69ad1a9d4ed32772803fb5111a015dd4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -26,6 +26,9 @@ import java.util.Optional; + import java.util.Set; + import java.util.UUID; + import java.util.WeakHashMap; ++import java.util.concurrent.CompletableFuture; ++import java.util.concurrent.ExecutorService; ++import java.util.concurrent.Executors; + import java.util.logging.Level; + import java.util.logging.Logger; + import javax.annotation.Nullable; +@@ -146,6 +149,7 @@ import org.bukkit.event.player.PlayerUnregisterChannelEvent; + import org.bukkit.inventory.EquipmentSlot; + import org.bukkit.inventory.InventoryView.Property; + import org.bukkit.inventory.ItemStack; ++import org.bukkit.inventory.meta.SkullMeta; + import org.bukkit.map.MapCursor; + import org.bukkit.map.MapView; + import org.bukkit.metadata.MetadataValue; +@@ -3003,4 +3007,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + return this.spigot; + } + // Spigot end ++ ++ // KeYi start ++ @Override ++ public ItemStack getSkull() { ++ ItemStack skull = new ItemStack(Material.PLAYER_HEAD, 1); ++ SkullMeta meta = (SkullMeta) skull.getItemMeta(); ++ ++ meta.setOwningPlayer(this); ++ ++ skull.setItemMeta(meta); ++ ++ return skull; ++ } ++ ++ @Override ++ public CompletableFuture getSkullAsynchronously() { ++ ExecutorService executorService = Executors.newCachedThreadPool(); ++ ++ CompletableFuture future = (CompletableFuture) executorService.submit(() -> getSkull()); ++ executorService.shutdown(); ++ ++ return future; ++ } ++ // KeYi end + } diff --git a/patches/server/0036-KeYi-Reduce-sensor-work.patch b/patches/server/0036-KeYi-Reduce-sensor-work.patch new file mode 100644 index 00000000..f41ffb80 --- /dev/null +++ b/patches/server/0036-KeYi-Reduce-sensor-work.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: peaches94 +Date: Sun, 10 Jul 2022 15:44:38 -0500 +Subject: [PATCH] KeYi: Reduce sensor work + +Original license: MIT +Original project: https://github.com/KeYiMC/KeYi + +Original code by Bloom-host, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/Bloom-host/Petal + +this patch is focused around the sensors used for ai +delete the line of sight cache less often and use a faster nearby comparison + +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index 94b45579dc371ee980565aed2f5dee78ebd44427..48746d84f18cc8ee2f57785c65a5659ced454d39 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -873,10 +873,10 @@ public abstract class Mob extends LivingEntity { + return; + } + // Paper end ++ int i = this.level.getServer().getTickCount() + this.getId(); // petal - move up + this.level.getProfiler().push("sensing"); +- this.sensing.tick(); ++ if (i % 10 == 0) this.sensing.tick(); // petal - only refresh line of sight cache every half second + this.level.getProfiler().pop(); +- int i = this.level.getServer().getTickCount() + this.getId(); + + if (i % 2 != 0 && this.tickCount > 1) { + this.level.getProfiler().push("targetSelector"); diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..1c84bdf2 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,15 @@ +import java.util.Locale + +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://repo.papermc.io/repository/maven-public/") + } +} + +rootProject.name = "leaf" +for (name in listOf("Leaf-API", "Leaf-Server")) { + val projName = name.toLowerCase(Locale.ENGLISH) + include(projName) + findProject(":$projName")!!.projectDir = file(name) +}