9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00

Initial Leaf 1.19.3

This commit is contained in:
Dreeam
2022-12-20 12:31:01 -05:00
parent c2693ea49d
commit fcd4bc6648
58 changed files with 6034 additions and 11 deletions

30
.github/workflows/ver1193.yml vendored Normal file
View File

@@ -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

63
.gitignore vendored
View File

@@ -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/

449
CONTRIBUTING.md Normal file
View File

@@ -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 <https://github.com/isaacs/github/issues/1681> 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:
<https://git-scm.com/docs/gittutorial>.
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.
<!--You can also run `./paper server` or `./paper api` for these same directories
respectively.
1. You can also run `./paper setup`, which allows you to type `paper <command>`
from anywhere in the Paper structure in most cases.-->
`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 <hashOfPatchToFix>`;
- 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 <theboyetronic@gmail.com>
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:
<https://docs.microsoft.com/en-us/windows/wsl/install>
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:
> <https://docs.microsoft.com/en-us/windows/wsl/filesystems#view-your-current-directory-in-windows-file-explorer>
[MappingViewer]: https://nms.screamingsandals.org/

23
LICENSE Normal file
View File

@@ -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.

BIN
Leaf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -1,2 +1,58 @@
# Leaf
Personal fork of Purpur, A Minecraft Server Software for Winds Network
<img src="Leaf.png" alt="Leaf logo" align="right" width="400">
<div align="center">
## 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)
<h5>Leaf is a drop-in replacement for <a href="https://github.com/pufferfish-gg/Pufferfish">Pufferfish</a> servers designed for fix some bugs and customize, and performance built on top of <a href="https://github.com/pufferfish-gg/Pufferfish">Pufferfish</a>.</h5>
<h8>Logo created by <a href="https://github.com/naiximcn">NaixiNana</a></h8>
</div>
## 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)

80
build.gradle.kts Normal file
View File

@@ -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<JavaCompile> {
options.encoding = Charsets.UTF_8.name()
options.release.set(17)
}
tasks.withType<Javadoc> {
options.encoding = Charsets.UTF_8.name()
}
tasks.withType<ProcessResources> {
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"))
}
}
}

9
gradle.properties Normal file
View File

@@ -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

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -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

244
gradlew vendored Normal file
View File

@@ -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" "$@"

92
gradlew.bat vendored Normal file
View File

@@ -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

View File

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

View File

@@ -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<PublishingExtension> {
@@ -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)
}

View File

@@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
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
}

View File

@@ -0,0 +1,98 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
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", "<black>")
+ .replace("&1", "<dark_blue>")
+ .replace("&2", "<dark_green>")
+ .replace("&3", "<dark_aqua>")
+ .replace("&4", "<dark_red>")
+ .replace("&5", "<dark_purple>")
+ .replace("&6", "<gold>")
+ .replace("&7", "<grey>")
+ .replace("&8", "<dark_grey>")
+ .replace("&9", "<blue>")
+ .replace("&a", "<green>")
+ .replace("&b", "<aqua>")
+ .replace("&c", "<red>")
+ .replace("&d", "<light_purple>")
+ .replace("&e", "<yellow>")
+ .replace("&f", "<white>")
+ .replace("&k", "<obf>")
+ .replace("&l", "<b>")
+ .replace("&m", "<st>")
+ .replace("&n", "<u>")
+ .replace("&o", "<i>")
+ .replace("&r", "<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
}

View File

@@ -0,0 +1,118 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Parker Hawke <hawkeboyz2@hotmail.com>
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 <oharass@bk.ru>
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<String> 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
}

View File

@@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@Gmail.com>
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
}

View File

@@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bjarne Koll <lynxplay101@gmail.com>
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();

View File

@@ -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;
}
-
}

View File

@@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: nostalgic853 <yuu8583@proton.me>
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<ItemStack> getSkullAsynchronously();
+ // KeYi end
}

View File

@@ -0,0 +1,683 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Encode42 <me@encode42.dev>
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<TickTa
shutdownThread = Thread.currentThread();
org.spigotmc.WatchdogThread.doStop(); // Paper
if (!isSameThread()) {
- MinecraftServer.LOGGER.info("Stopping main thread (Ignore any thread death message you see! - DO NOT REPORT THREAD DEATH TO PAPER)");
+ MinecraftServer.LOGGER.info("Stopping main thread (Ignore any thread death message you see! - DO NOT REPORT THREAD DEATH TO LEAF)"); // Leaf
while (this.getRunningThread().isAlive()) {
this.getRunningThread().stop();
try {
@@ -1656,7 +1656,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@DontObfuscate
public String getServerModName() {
- return "Pufferfish"; // Pufferfish - Pufferfish > // 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<P`H(KL6-v!xPS+l>-<@x#R
z-`C%VIfrjk-qRQmOe~tjgP%5lFKS_e{L5tV%CZBaT@Y9t*)1R#bTIzM5`GW>xPtTc
z3UI3vd4s8S1i%8E7FnpUhPVHPbg0m10l<lXEefo|PcR8;fDx8&BcA~y_<%a~Ml%#Z
z3IJf|?I%tFs6Ydp-N^~V1D4B^*YE(#KZ(nc0I@KDOgb4VSkX>^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(<E-0VhRZ_GY&{$EX$PyDFz=$epaOz9C0azEf=>*)R(2aVRrfa
zCHA}MO9FHR%n|h#^;?ELC8$9aZUNR^wEY(uVboB8>#Q3WhCU?ir|m3Z6R8lS<!9OK
z*CTf~fPYk+DP)!%-6Cei_<Kx*l+h1uLYiS*(|v7j&3JB85^Y`&9HQPl<4Yte*k}m$
z8InOL=#O1+LV}F8g0{pj>NCnFtPUhC<d9JIKeQCdzm%J(eXyPfyiEA(6SSqPXfDV}
z$@8cUXwk6^lVz#UN0auHP{^qhIOIOb*~&s?nTyqnh37!lISMqV3HGBKdq@qCDsXyY
zYXU}sa?<N)|6c27{=vu{1>~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<B&T~RJ3rk
zkYQO9Is#h)UxG6HRsz;cg;MzW^vscOY=1e3GA%POG8{Fu86Ft?a1zI`Qu$LgQv*^D
zE52#CYKSa)SA6<bsew^xpy^*Kt<k8mTaLVdTe(<HqS0B-Q!}OJsTQv?t!h}*ZTKGi
zVv5!f9wuE7E?e}?v~`FemK=A8!~9xXe91)<?v%+kL(xR9;rUF#1OAoBgVk3-UBb1(
zqRJxCBKN>~5(a&!e(UPXN{;*#`4rleFEbpo2y^|CO|?aJKsE2G&uaIo$+^op?)mBJ
zrMpwEXf9LkJ-<EwqdRg3PlswpSO?U7`b2o%^wj$p`F`vk{dj#==HC9U=pOIna_MrG
zjF*iIi6?=;j-Ai+Y9hh($Tq{eZT2d3`xoZ8RY&50Rne?m5p5gIMTF%pw-=tUMU^A5
zNfp<I`I@buv57SYw0NL+aIc$wGQ~E_qGPsc-fd!HglVm6xoY$*?#p71ZJu%W%5Ugf
zY*>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<Wx!HEW&q+_-mAdN
z_<Q_Q&$jms?zIxU5lkP<K1^2-VNfHSEgTsz6Ig=C4Ic~NiNJ~~j3bVUjHW~8lI+-v
zcSH1S%DHCAlr@VphLnq=OZ37vL8Cyrgc%n;jkF00a4daRIG$)hirkX1i)<saCdkBl
z_Ww-Jz)`_+qS~bH=Ir*!v{bdH^J>`@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<Qf7x2;T9qvr8I#fWybtWh
zh>|159``7pU2mZ{c3}#$MNMioD(JWhtiKq&^zik(+GM+Abr(1{Ej~`3DL)0{`gJAL
zskGHKF1LB<TyIhINOj^;Myum_vasu`b(ncsU(NZCTa7oQy{qhKt7+WWc(2{WsfEmy
zlt?fJYjLHPr;Ls})ZFQ|o8lO!nrU3xq%Z1e1~TBK8df<~{i=DYlWsfM(NZ#8^t<=A
zUtQghZyh^7l|3y!b*OsO>8X|PvJqRH3BN+CCc2K@Co;5`-*7ARvAK_%Y?>UKgyS~m
zKG&OQse;}V@)SMz<Ob@GYra=kwsx=3tS<Ged-~p@hmhb1Y+R<C{cdLT(z5N6^1pYO
zy9i#7Cp01I=s5^jZ<Fy<9?XmPE%8Nskh(mzadWg=tL^D&K(<%oi6xcgciPW}IId=&
zXL&X|=`x`I5!>&h-7NL<-Bmd%oa$fepWMt0Y`sT1mGx$v<L_~=VlJ{T5Gw4tJc=pG
zB{3k8>2CcOaLK-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<<CGon@pnswqf>W=#-kB4(SPm~LVHGQ9WuiCfn)%O#`
z7mSm@rjL5_Ui+;lqh6!wejSWUbmWM<kCcD&5!-Vz6EcgG(v-3BxnyKSXoOuiW>d-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<mBY|`1r`k$nfy+z`($tKY#lA`g(eLy1KeLIy&0h+gn>(
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{RMH<e`JI_0cWV2En%hAkZIMLm1)yLdS%$kTQ8)AJx1qM1%!+nGU
zz<V%#`(oRY3iIP1__~a=_w{EW!cAe~i#mpp5d|9nHmH4b+n42RU_J0cz?OY$dg$G_
zT38aor7zw2SF~3=2?6zj5d_QFU-hTn=yW0hG5ojZAEn?QNK9d}EJ6m6JXtK;!c$27
ze|z#${hqwm8Xl+m+R}BSsl{~1<b|KD-oDn(vWxE+)KF5zHgF|j<j2roUL7~WBFFmD
zQ9_Vn6=_Vz<L90O{qO7=D}Fp}Od_!Z5r;nP8s_oku*n4VS$zSSzBH7ul13;BQ$iUT
za))bOWklxv3NgN=%}7k#dgbNhDB3HFA+@}=q!KqmKye6uR`&Pty{p0P+v>3}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*<YdfRV>N1JpLTJ>Tz117n7S<sJ;
zT!;u=hnoGy<?bRAjjS3SHmyY1KNLV<EBHI)F9NaheVa`QJ3AzTDh6LzCaz&rn^Si`
z7KK8-JY6C(v&nhYj*djIoJFlbB%+l~*cR4=9NB}oFLGn(tUy2KUFngHqlTFSa0)u5
z$@oCKK`W#Nd6U?ht1>d@M2K&N<B>*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_F<X6ZFE{$;v>Ay-r0%b`}E(GtxF~
zv|cP%b87Zbc~JQGSpb58hXhj`D?6opux>N1Sy?vhUh7-H>i<hL1Gy^P(z0kpUMgJk
zB%%Zz=@AQwoU5HHT&Z2i&V0(#21pNZ?vE7t1_mq>>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%x6CI<q^m4K_jHJlG^()kLMx~C1tVdhH=TX+Z)estmHew`f<M-Pn@Uw%k&fF
zM8caM&3>m--w;HNm)6XRpe60!>b7QP&iJX$IeIqT+6wKbXLRk>@)YAGq80Y{5{--z
zdUTSsK<AArtRIPT)ip{u!iMP(CnE*nb9eCF+qyp7h?dRuHNRLd0ub09?MRFh0|fEL
zQvbGQ75*qxIDA@AUf*D!dqf*hW)Xt`#gGyb7O0&wi$Rh?|FR=RC4)t=mS%QtRBGBA
z4SJWokYaZ$9BDVEbS{O=#`Zd=CybEDQ&Y71;nz(_!;KL*ZB4nh5JB+lI@kNd<r9Yj
zMDUJQOsR(J{Py(=n?fLp3!0>26d8)bI{R{m-gW^jkyhhnd<%QQviY0uJVR_mXv%{J
zWJ-IQ4~A!p=iApvClQuYwlJq37y;9w0JfAC%Gtti90aIQT6rK(Wh02##Kgo<TW-r=
z_SIWd0sB^LdDeJ37Dszro5EocrR?*-@0X~+y{<-FQBcsxm9`HZL5dEz=Vn#T8*udg
zr3|g!g6GU9uL!GNVd;9VP+Dh#N>xP2wPXL?bH+CxGu(bqnr5#2SFV_n?~BG!e;}4;
zAO#jU0rcnJ<Zx=yVh-C7TCc5lpJDBV|9unOPiS@W<ELVQf8rwpBMWXEIT9`NHO++f
zhZP^RJpnPYpu`kSu$5j^BruU;M1gdIV9y<QMD_Ie-+FRuEt!xHYn7Byf{_F;Dn>~;
zK3vX>Q06d9t!w*+JL1>lL1biT3I{&wGL<a7IpQCB{JzkfQf_G)M0!{j$^5m$Q&X=;
zf@A3yYN8}fy8vs7;w=p&C6qxQz0M=nilj`Kz~clas?Y=q!AC2mkl#V%;s~|_gO;pH
z#4d;ggRd~+1E}PG)IT+j0&-5SuT|SOj1fk)N(Z^oO}`JyGR={R5KIAVrIR7r@ClW_
z-(Gr2LfbbYVxG>$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@h4<H^gFP<1Np`Njt%3q4z8ON;`XJ9TP*jmk7)sy+=LN^Nh)?eFOaCCmUp;{DOl
z7<Nt5Ev-t~q~H}uMkMuDdSnndq1H<d6q+)pHr8?`+S@=~4^3#4`2K$IXvt(J#zqpa
z;dC)-BJ22`UyUB=XsEa$_m09Q0U{#E(#|3pvn2G@doAG+Bix*zv@oR}rdS%Bn{5s*
z`6f<Jo{A+e4p=)t()FE|Cv8Hc=!aOJRXl~f87k)p_<fb$UJt?taUpA7m+wEzdfdAO
zn)aLrJtbvf&JGT<!$U?5Co)69_~cuRG=Py{khsPE)!+|_wPr?SgzB!@y(~^|73Yhc
z98WW57`Wxp_K0M$rg&bDPt0{YUxSDmr2p;9c!Sg1-8Ay*QTEUzY)y%$`6#fOLce^x
zTTC7!r-Jb{O`fMM&5kQ;89Zb6av?~Rvi<TR-!q;#wAS@DnLSsukF`o`<lnbci)}5Q
zEbxf>hw6CpJoJZ;`Ql7V%7*QP^*31;IyLIng;k_ab_1`Wta<+~DKEoQ@4Eox2p~CD
z*0K`yjn!5AMo>5!xB~*@6fa7~%<j|_Xk?!VUzyKyjy<)W=42{MKTQ6<Tt#yN4SPwY
zrE9-SSHIZ`bl|zD3)-Aq!eV<EDcb+W<JwZMY9-HO=Cr6=vgAC96%LP@LC|f8v`q{A
zA(SAmI6<Wq*r1VPV55Qti@Lc8lsI*<-qQ*sX}ritXa$OcEku171aJ0?DLQ?w+#0Q9
z;YeA^*id_q`0x<?ddz3zm{@2g4^OkVC1q)X<vE=lXA|;%;e8JchZ|FG9U|Ohc>=#3
zU@OB91zyo5zT2H*u%hsagt5*yo2rqwJXz5lUI>cNA{*;ZrgO$Xtt_PYsE*??(M=}7
z!Kjn|zXem?%?_clKif+dx><n$Fb8p-pCG2TkWamOh;9&;))Xg1ubKce{?%i~7x53s
zC{_EZ&*ghy!|kNS;+4_Iv<5}r(q6wDG^cynAqe@e5QZk(WcaA`&zrd3B`8X#QHyD`
zGL1b~Yrg$oNiF4jc-O_PE(PgO8PX|Wd&hgj9z4tz_ZP4Oui&z3<@+#FCI_bE$#uTs
z`&^3QTR^86#D_>oN%Q1dEv1sx@Uu`-G5J$7G()L>^oP}zziqVvcVw>iixpF=Ps{6B
zuQ5j}{LBk6xiS90($ya|`Bu**41tAe^=_0<)MZ<Kl@|Yq``1!Y**|bWW;@$5lubS_
zZ~NXvMn(|P;nY`A0&IMrJE|WK8!yI)M(3Uc`a<zPR-D1b1<<lRMV+r!7)A|AUzLY$
zU!GQO-w&WypKWQ<%4L*4YzzGF&OLT{rp!!5cUb<Lf%A`ok&4^(D1HO&be_h9{#S=U
z3XjE+{$YAA+G$T4={fh(jyqY?i~+?GA|W4BV_JIA#OGmCZ`Z5WdsoW>HlzI6M0ug-
z4efyaE$C!Nkh3Gg9NKD+W#E8lW6ZR0CKhAarf@Cib*y`SD;D&4Kf?Ou?dUYN;w#`f
zcEvIYw#u<W?07bN8VQu;#aB-=)xA>X-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=ZuiVPT<G4njg!cQrV{_(r%%u@?Ok!7HAVtclaa!Ocq?_j|AN
zf$2heh9aJFKlE%k@lkP5s*d7(5fOXnK?IpP3g+2RW#yb`RACs<(0~8A7*q|IfOVI(
z!wc?3N=h7!7tX^tjAG@LCmp%6m!RSqOS@XV@HJ-+Cd~CscpdC}F@U=Gq9h`NF=gYI
z&pMPk*KIuU{dEO_-bTNcCw9yP^D8d|1s>mw7MY{T*;A;uJuYrm5D~;t6C_=XIr4>;
zmu;`5dT`;u@`46+%X}S+6?kLKO2d8kD2zN{>oUB9p}^*c!Rw`$%zi4=<Tme61+e+(
zT4vcL8yEp$hcUHxL4m_iXt{#T7w@MX;HRhy6Wm^CWv*_sHh*Q4e$ji51gjH6Q%oEy
z3)Pw3yPNOsn)$=76K}jKAyPTu9H!M*Gh{k)Mh%L6E0M{H-Xi~UBFKq9nY>5a!o~DA
zL2;T(zT`Gpx3dWCb4Vt@nvPG9lQ$8fz6=$v@L?L+a*uu-QztN^$I2V`>s;jH1Hpwn
z?YPl-zTJ$RO69&u0C`FqHt<pO#?U{wMjp~fS}MYB)!LVw4ZrkJaIlMTzJ2xc`CYGW
zx5<HlX9iGf;mYe~wBTn0QEE$;mF}$MpPz9r7Z7P(iW^(G>LSw@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<BLr~-#tLvkb;Aw
zz=$9H;z|Yq(WENz`3GwwEh`4pubOY*pN42ZzDv6<DiE9Uc)@8#+k5Y~X7hLZu_J8;
zNygaN_gs_g!e!Ky9pE=QK<)Vv(d*|85Yp(tj-Olg?@s+wOFEBv-XRe3E??}JHPx4_
zs9|9=GsqdfC*QAzFJ&FR3!oO~<+}WowrG3?BZ<x$Z_j*vB;`Krr6&`?$L9~95>|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(<cB?^w(
zdpb|jN>v*J-vk2)bk@|=F2Rlp8)^hr=RBS$lhNa5Ch*kKc<NM?#SCEM<i?sZ%TCSe
z_qf_zS%h%;g=m$p=Xh54LSh=2t#g)gvwQ$xAe&Sf>E2)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<kof8&fRyqWM&|w?!s+#Qo8fJ!PQym?>-Gg
zR-$6>Y)w+ZOjtl<S=gajY(gy=2jdUp;?v77e1Pu|WB;ww^CW0IGWfXdWxZc)f`P4>
zFXi3spSe!tazAZarF+?8Rq9Ez^f)JJ=<y9JoPvUQ)K$oHW@2;I$ML~ZK>KNl4Y_zd
zVD<h1?V9q|Ct(6fpkS*-@F5xf5*8>fNh`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<IrLP@C7YR&GTjD{?1ocBZZEVdK88Y~2?hp-;vN4=XN^
z+sRZNq|L}7j5CpMpYM%jmLtAyB48v#nfOicrMx5+CE0S#r^rA+sJtaH!yw~p6@{Kx
zxAVq>=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<<RuTGTF<yp82hbxBMbnn&2(WOmhwV7@>1rp${96w#J
ztP~I0n%NA8O*MN?P<+;S?hhZXE#K4|0;!1(U(WfT63ha@eDz$vUyfe_`junc9W5fa
z_^4#HgGBjA<A?tFvq5fu9PMpV&WqzuR(O_7U;xU^6ueSbbGgmZotK?IIjnQHF=Dv>
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&U0gsc3VvdUy7fA4e<x043J4fFluq`he{i=SAR%dqOS?0J1x)JtgGaGz
zJqgCldD`%!+FW}%vF=M`A8+I4!K+Oa1zOYoVJy`(hfL<m0eVJ<N#&ux^4+;!?D5>f
z9B@5o?zgVpsI<K5RcUQ6UV~0%AKG~kuFU@SQSLH=adO_QzN9>xy|0KRS8K~UH|~Gu
zxWR|j9m0a)8Yh99sm=_&poSFAc)8f$TbH~3UEzhD1&UHxT3~19pWERGnU~Q@Ts$2e
z7!*+!dm48og=(to#vca8_N@5T{{?*G0<TH;_cKZi<l`sG9xOe`2b6Duy=9CSW@M4>
zlgGf#*Y80{eG1zvE8AyftcJwCe{OA3XVNoOOsr0;=#P^AjuIL;osF8<Ro8z^8p9<p
zm?S&6vDD5PS^U!eY)}%gxY?G7G*B0g#nGxqY$ooIfVs2^9Ks#Ct&GrnWfpUK$UhHI
z>dE~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<RcX
z7MojfDbnTm91KCW_MHv?g}`9e|CnGZZm52rG^r(;uFe<w_x?Z|ITgr7WQ_2YT4>~H
zAUQ%}Won8HU0-BZNlfpLwV|sbvgUu9xXmPI2>EQ5)Q%V*l%KI3&fHEa-%##X@6pXf
zy6s?f$itx80{!nNkJrPeh>~G!a)1$<t<^Q+IQ{+eMJx24<#1eu&NORkf89Bf9HfAt
zxmq&RYmUVfvWszc_Us(XE*m?-y+mPRjIU`E@WQ)tJ9+-VIQp4scKQ15`^LoaK_QHU
zZh3nNB1~=LzldC=OJfGBep`2zTW%QXFl=1i#UNWJV07Vin^jJZZ-~B&y>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>?=!ttolM<SU8Ge2n)4JGBL#-ZE
zoP46`T2jYzo-j-y1atBx@(bcP#Y08yOe4FK|1RQRL7h!H->gvDca<Lv`(uV*4&-6j
zLSPd`yR~iC7bniKgXjhG&|UU_nJ_tm?|sjQfFKH2^+G>$&z@0)0}Yd((7meDa~unt
z?6;%gTjPb+kL%O<a73ON1kiv-#&(O4@q|=*tA#cF&WpDtq@Z2}1R=~LoZ=gfAl=L)
zxIT6_F6(_Vk#EM}iT1xNXrA(=!OYJk#w~5TH7RqI6Cn5nVP5hO2-v7N>;jbP*UqT+
zwL^77jqppC&&-_8t$I#2RB4@@SDKCGf8Cvh82ZpchjY8(ze#h_3I4lQV>rzfezj>`
zQP&EN1Xx3L<{b<fd(;+|U)C@4(k^7to+M_(!F)UdO}bE_t1uf-Uk4ARLS5N^rrXVh
z!M_t#DOko8ZNZ%W&5N2ILI1_uxj}k39tFKd7()hj;|pTk@?&}bRu=P7-BJsg#!7)s
z4LBap6u~=0Sk3ax#4|kF*Ts*svsE!)G*x5aS|R9$ExW!fMToX;A|R*K^iAZ6DmHuu
zZOIP7t9?Lvz_OIG{pLe18-XJ==<gz{%ppy+-S&Rad|-d?){!J}W$4nwW%p`@&p`Uv
zc%-NY7oc^0d;G0e9MZfL+@ZRq_d|~Q@IN@s)U{@Fr9T2YqQQTWp-4G$A(H?6?CU2b
zTC<E7<mf<JJj+{vQfmu(c8%lTZWQ;R!Vq%g){`xths+h;O~*bl2v>Nq`~<7wJ}07H
zSR9#yKjnW0c|oLtA49d<ah=b#^e7f8ePk$VT;QclJoZS@^$m2r#@%18j7>BMP~EJD
zXKnKj@EG^_S5&;$^FWYNqJKQ_x_`xRKS~d(_`P!)Ec(XRR$qF!llii3jCd!{q!ZF?
z+!8_d{xp2sgVT<a@(@SpFK&TKfNJSB?%gkMWE%Nar*qVI6Ce0yb~bKj?b@}qUO&2m
zb5f6Mn{dz@5|+4jGUYJYsq*D4kqs3`X__epfip8}>@;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}My<EKA_VLQ}Xc2ZAII;0b^D|__v>NXFtKVOM90amFI~|vbs7PP?
z+CrR`_O);#spc?-b_==9^akfc`sLSQdP9RI2EU5RHjo2p^J6L&cgs5S5b*>1S=i2w
zme<S}GA08(e)qV4YlXideY*b%la^-~e(t?dEgOkEFlGgdPbD$Cw|wNpL07jb_(fQ_
z;;(+dqW2q(DAHMwf|;8$Md-XG^gfTDf~{(KWMI!r`+98jqffP-db0ZdVQBU$2g(uf
zVS(|Hc051<Qj95;-chyXhC1Zte4b*6vjkrp`5a?}!sDw^<q1Z)c-t140UHrA(A3?$
z0%%Ak_XYNoLqc5U)p;Ftdl3*E_TS1P_MuFUCLq*e+$h*>SqAQ$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%>`8Mn3<Hm(s$1&0rFFzb+(!3uO
zd0C<V8!eIzNtw0e){t0G$f|EsAfzz3d~knTw8go*$J5{*RgyK<Q(^_56TF7&d$W}D
z8GBsT%*>C80Ii1NF?z^iyXu>jwS^pEci}%=dp#5gh@zRV+-3MwKm(}<PIq*9@9nPc
zPQLTBOk)=j+u+<{pg=f;8#Np*qY{0f5?d#MrclGmN>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|CyMGp<xrRhrGJYO<Wv6B4upM@iI+Pa8-BzqWrY
zkI^E&Dw@C&B>G)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<Jj&MJdFm^JP(WeC1>_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*<XgR0Juyy>y)lF(Va3vLZ((wwDgPD|>vYdO<*}cL{-yL%L-)REcsU!Aw$;D1?%k
zscDa?>1lcUX>&p<`1=FVpQA@7%1WO|>vOep@;|tRh5WnS4+<S`8El8C%Yh+J2Q>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<<P5&t~%|Q6>S)TUalSDf=YQ~qKV_K`jnwyhu
zOQMBz9ypM&Ide}$Jd*!jiQM?wqckLvrO>zHTd(irk5S?%Xo!RzwK7>@2{sZ1TEM_r
z<HJhp{uprbaicT(ZzI-=QDvzvE3>+H1~K{XACzG7KW9A#byMA6a^J<+ZjUz_t2?0F
z298^%FiBOujZ#KP#$dt0KPWWn$9GOtby<Hq)P-4d(<`Tpkjoxn%#K)vKh6&$WGJH>
zS{~hM59$Hf4l2y)QHJl_`P-M;W_o>+E_6blKkX;D3f%AL6smaYKcb=FZ{kG0?@gDP
zoyYX&-d3-pyd`C3yA@xVBBs+u<Ew>7$Rr<2(plAlp)6X;<Cgm^-pR7tomiM`FYlNC
z4x5#_beKs~2uXI=OVw)H6;P2%g0BaztoT2tm~bC1Fof;iHk^8|>n4cA+4Tss1%Jcn
zZsntDp0{55S|VSv<WbDd8(D1TRNF3%Pe*_%6_GqD$;8w+;-P#0=h(Vr+x_(LNdR7)
zm>{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;{
z<xjB|z6A{RB3K|~`><q*Q7aD8=ITYiN}Y{O#uk3a#YLesl|k4BJZB1uBo<|V|LlJe
zWdl#$RFmT-%}cMBN%w_8csEPOK@w3}!tkbZBc?<pDy%|cj^;nl$KKVuHo-kQI`Mz0
zF_{EX6ym9Oj8mni&KDRL)yfx~nhrc0gf=EusBb6*#6Q6?f+9=jD2v#elWa8@26Y>E
zz48#(Ii>N%SNbF=G8H7NQ0rD}(xV1V8ZW$fts^d@B7I7&YE!6^vIxJ*+>Qv4w5~r0
zPP0oBU0nE4z-~M75!d~9tT{gI+6<b|yoXL?;A2B>Agrt;1W<oXt4Y|aR7@kIKHCGg
zltECH*_q7<3$~UMGpf~K#APUPfgU@=z?)pK4|_JI^&o_{K^0{I5^RhDIr1gaSS?Aw
zpNoumRY9@4P4b6f;w!VIcqt&saNl$4Knn2tM}>Hz83!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-<bn8*TJiuj1&UCGt1CkssU-hfK
z{K%*};eJcw87m;L@O<lfA?ax)hFPD>x_fXATv`8m+@*Z4xNr`IXOZ^5U#Vq~aggtL
zpj~`V8}OuQZC#*ypt@f4c68emktTjn79)H4+lyF=aT)U6QSKk=o<z|<#Pt(u*yo-x
Sz5noP068fo$r^E^;Qs?Ck;k(D
literal 14310
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%0ABLt+m?son?j
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
WX&3$!%>|s_1IbA#OV)^+1pg1OmmZn`

View File

@@ -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<TickTa
@DontObfuscate
public String getServerModName() {
- return "Leaf"; // Leaf - Leaf > // 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<String> getStringList(String key, List<String> defaultValue, String... comment) {
+ return getStringList(key, null, defaultValue, comment);
+ }
+
+ private static List<String> getStringList(String key, @Nullable String oldKey, List<String> 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);
+ }
+}

View File

@@ -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()
+}

View File

@@ -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");
+
+ }
}

View File

@@ -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");
}
}

View File

@@ -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);

View File

@@ -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");
}
}

View File

@@ -0,0 +1,548 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
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<S> implements Comparable<CommandNode<S>> {
private final boolean forks;
private Command<S> command;
public LiteralCommandNode<CommandSourceStack> 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<TickTa
this.safeShutdown(waitForShutdown, false);
}
public void safeShutdown(boolean waitForShutdown, boolean isRestarting) {
+ org.dreeam.leaf.tasks.BossBarTask.stopAll(); // Purpur
this.isRestarting = isRestarting;
this.hasLoggedStop = true; // Paper
if (isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index a7023765e3c82e70574069af00227e3cf6f98e65..598d0200c055ef357a0a0d8857ce740d0bff9bae 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -50,6 +50,7 @@ import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.block.entity.SkullBlockEntity;
import net.minecraft.world.level.storage.LevelStorageSource;
+import org.dreeam.leaf.tasks.BossBarTask;
import org.slf4j.Logger;
// CraftBukkit start
@@ -336,6 +337,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
}
if (gg.pufferfish.pufferfish.PufferfishConfig.enableAsyncMobSpawning) mobSpawnExecutor.start(); // Pufferfish
+ BossBarTask.startAll(); // Purpur
return true;
}
}
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index c0b0a7fdb75266a7064d54bda6441953184ecc64..3db273840ecc03e7bf3e1a01f56d5128e4aeea06 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -270,6 +270,7 @@ public class ServerPlayer extends Player {
public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> 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<CommandSourceStack> 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 = "<green>Tpsbar toggled <onoff> for <target>";
+
+ private static void messages() {
+ tpsbarCommandOutput = getString("settings.messages.tpsbar-command-output", tpsbarCommandOutput);
+ }
+
+ public static String commandTPSBarTitle = "<gray>TPS<yellow>:</yellow> <tps> MSPT<yellow>:</yellow> <mspt> Ping<yellow>:</yellow> <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 = "<gradient:#55ff55:#00aa00><text></gradient>";
+ public static String commandTPSBarTextColorMedium = "<gradient:#ffff55:#ffaa00><text></gradient>";
+ public static String commandTPSBarTextColorLow = "<gradient:#ff5555:#aa0000><text></gradient>";
+ 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<CommandSourceStack> 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<ServerPlayer> 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<UUID, BossBar> bossbars = new HashMap<>();
+ private boolean started;
+
+ abstract BossBar createBossBar();
+
+ abstract void updateBossBar(BossBar bossbar, Player player);
+
+ @Override
+ public void run() {
+ Iterator<Map.Entry<UUID, BossBar>> iter = bossbars.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry<UUID, BossBar> 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
+ }
+}

View File

@@ -0,0 +1,234 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
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 = "<gradient:#ffff55:#ffaa00><text></gradient>";
public static String commandTPSBarTextColorLow = "<gradient:#ff5555:#aa0000><text></gradient>";
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<CommandSourceStack> 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;
+ }
+}

View File

@@ -0,0 +1,345 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <blake.galbreath@gmail.com>
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 = "<green>Ram Usage: <used>/<xmx> (<percent>)";
+ public static String rambarCommandOutput = "<green>Rambar toggled <onoff> for <target>";
+ public static String commandRamBarTitle = "<gray>Ram<yellow>:</yellow> <used>/<xmx> (<percent>)";
+ 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 = "<gradient:#55ff55:#00aa00><text></gradient>";
+ public static String commandRamBarTextColorMedium = "<gradient:#ffff55:#ffaa00><text></gradient>";
+ public static String commandRamBarTextColorLow = "<gradient:#ff5555:#aa0000><text></gradient>";
+ 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<CommandSourceStack> 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<ServerPlayer> 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<CommandSourceStack> 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;
+ }
+}

View File

@@ -0,0 +1,58 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
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<TickTa
// Spigot end
public final io.papermc.paper.configuration.PaperConfigurations paperConfigurations;
public static long currentTickLong = 0L; // Paper
+ public boolean lagging = false; // Purpur
public volatile Thread shutdownThread; // Paper
public volatile boolean abnormalExit = false; // Paper
@@ -1159,6 +1160,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.recentTps[1] = tps5.getAverage();
this.recentTps[2] = tps15.getAverage();
// Paper end
+ lagging = recentTps[0] < org.dreeam.leaf.LeafConfig.laggingThreshold; // Purpur
tickSection = curTime;
}
// Spigot end
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 07b40f503358987339937bb8db5a3ee54c1b08c8..faff1f560eef2429621b4b0a4d760f9a558dcb73 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2943,4 +2943,11 @@ public final class CraftServer implements Server {
}
// Paper end
+
+ // Purpur start
+ @Override
+ public boolean isLagging() {
+ return getServer().lagging;
+ }
+ // Purpur end
}
diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java
index afdee99c7d5ecedf8d2f6856f0aa9271c6bbb23d..a723d5a6bb69543072b8e9a7082ba3f0e89d453e 100644
--- a/src/main/java/org/dreeam/leaf/LeafConfig.java
+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java
@@ -222,4 +222,9 @@ public class LeafConfig {
private static void itemSettings() {
compassItemShowsBossBar = getBoolean("gameplay-mechanics.item.compass.holding-shows-bossbar", compassItemShowsBossBar);
}
+
+ public static double laggingThreshold = 19.0D;
+ private static void tickLoopSettings() {
+ laggingThreshold = getDouble("settings.lagging-threshold", laggingThreshold);
+ }
}

View File

@@ -0,0 +1,40 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
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<TickTa
this.profiler.popPush("nextTickWait");
this.mayHaveDelayedTasks = true;
this.delayedTasksMaxNextTickTime = Math.max(Util.getMillis() + 50L, this.nextTickTime);
+ // Purpur start - tps catchup
+ if (org.dreeam.leaf.LeafConfig.tpsCatchup) {
+ this.delayedTasksMaxNextTickTime = Math.max(Util.getMillis() + 50L, this.nextTickTime);
+ } else {
+ this.delayedTasksMaxNextTickTime = this.nextTickTime = curTime / 1000000L + 50L;
+ }
+ // Purpur end - tps catchup
this.waitUntilNextTick();
this.profiler.pop();
this.endMetricsRecordingTick();
diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java
index a723d5a6bb69543072b8e9a7082ba3f0e89d453e..66092bc87472278898743971dd80244cb750297e 100644
--- a/src/main/java/org/dreeam/leaf/LeafConfig.java
+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java
@@ -227,4 +227,9 @@ public class LeafConfig {
private static void tickLoopSettings() {
laggingThreshold = getDouble("settings.lagging-threshold", laggingThreshold);
}
+
+ public static boolean tpsCatchup = true;
+ private static void tpsCatchup() {
+ tpsCatchup = getBoolean("settings.tps-catchup", tpsCatchup);
+ }
}

View File

@@ -0,0 +1,85 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
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);
+ }
}

View File

@@ -0,0 +1,63 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Encode42 <me@encode42.dev>
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);
+ }
}

View File

@@ -0,0 +1,53 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: DoctaEnkoda <bierquejason@gmail.com>
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);
+ }
}

View File

@@ -0,0 +1,145 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: DoctaEnkoda <bierquejason@gmail.com>
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<Level> 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);
+ }
}

View File

@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: DoctaEnkoda <bierquejason@gmail.com>
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
}
}

View File

@@ -0,0 +1,79 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: DoctaEnkoda <bierquejason@gmail.com>
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);
+ }
}

View File

@@ -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);
+ }
}

View File

@@ -0,0 +1,38 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <blake.galbreath@gmail.com>
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();

View File

@@ -0,0 +1,25 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <blake.galbreath@gmail.com>
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<T extends Entity> 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
});
}

View File

@@ -0,0 +1,73 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
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);
+ }
}

View File

@@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
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<String> bukkit, RootCommandNode<SharedSuggestionProvider> rootcommandnode) {
// Paper end - Async command map building
new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(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));
}

View File

@@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
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);
+ }
}

View File

@@ -0,0 +1,129 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
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<Villager> getBrain() {
return (Brain<Villager>) 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);
+ }
}

View File

@@ -0,0 +1,35 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <blake.galbreath@gmail.com>
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);
+ }
}

View File

@@ -0,0 +1,52 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mykyta Komarnytskyy <nkomarn@hotmail.com>
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;

View File

@@ -0,0 +1,38 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: nostalgic853 <yuu8583@proton.me>
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

View File

@@ -0,0 +1,25 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: nostalgic853 <yuu8583@proton.me>
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
}

View File

@@ -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);
}

View File

@@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: nostalgic853 <yuu8583@proton.me>
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,

View File

@@ -0,0 +1,126 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: nostalgic853 <yuu8583@proton.me>
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<Biome> 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<Biome> getNoiseBiomeAtPosition(double x, double y, double z) {

View File

@@ -0,0 +1,87 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: nostalgic853 <yuu8583@proton.me>
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 <C extends Container, T extends Recipe<C>> Optional<T> getRecipeFor(RecipeType<T> type, C inventory, Level world) {
- // CraftBukkit start
- Optional<T> 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<C> recipe : this.byType(type).values()) {
+ count = 0;
+ if (recipe instanceof CustomRecipe) {
+ if (recipe.matches(inventory, world)) {
+ return (Optional<T>) Optional.of(recipe);
+ }
+ } else {
+ for (Ingredient ingredient : recipe.getIngredients())
+ if (ingredient != Ingredient.EMPTY) count++;
+ if (count == slots && recipe.matches(inventory, world)) {
+ return (Optional<T>) Optional.of(recipe);
+ }
+ }
+ }
+ return Optional.empty();
+ // KeYi end
}
public <C extends Container, T extends Recipe<C>> Optional<Pair<ResourceLocation, T>> getRecipeFor(RecipeType<T> type, C inventory, Level world, @Nullable ResourceLocation id) {
@@ -131,7 +140,7 @@ public class RecipeManager extends SimpleJsonResourceReloadListener {
}
public <C extends Container, T extends Recipe<C>> List<T> getAllRecipesFor(RecipeType<T> type) {
- return List.copyOf(this.byType(type).values());
+ return new ArrayList<>(this.byType(type).values()); // KeYi
}
public <C extends Container, T extends Recipe<C>> List<T> getRecipesFor(RecipeType<T> type, C inventory, Level world) {

View File

@@ -0,0 +1,530 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: nostalgic853 <yuu8583@proton.me>
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
+ * <p>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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.
+ * <p/>
+ * 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;

View File

@@ -0,0 +1,59 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: nostalgic853 <yuu8583@proton.me>
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<ItemStack> getSkullAsynchronously() {
+ ExecutorService executorService = Executors.newCachedThreadPool();
+
+ CompletableFuture<ItemStack> future = (CompletableFuture<ItemStack>) executorService.submit(() -> getSkull());
+ executorService.shutdown();
+
+ return future;
+ }
+ // KeYi end
}

View File

@@ -0,0 +1,31 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: peaches94 <peachescu94@gmail.com>
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");

15
settings.gradle.kts Normal file
View File

@@ -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)
}