Compare commits

..
1 Commits
Author SHA1 Message Date
MiniDigger fa64402087 rework bstats 2021-12-30 13:35:55 +01:00
2619 changed files with 150956 additions and 236556 deletions

No files matched your search

-22
View File
@@ -4,31 +4,9 @@ end_of_line=lf
insert_final_newline=true
indent_style=space
indent_size=4
ij_any_block_comment_add_space = false
ij_any_block_comment_at_first_column = false
ij_any_line_comment_at_first_column = false
ij_any_line_comment_add_space = true
[*.tiny]
indent_style=tab
[*.bat]
end_of_line=crlf
[*.yml]
indent_size=2
[*.patch]
trim_trailing_whitespace=false
[*.java]
ij_continuation_indent_size = 4
ij_java_class_count_to_use_import_on_demand = 999999
ij_java_insert_inner_class_imports = false
ij_java_names_count_to_use_import_on_demand = 999999
ij_java_imports_layout = *,|,$*
ij_java_generate_final_locals = true
ij_java_generate_final_parameters = true
[test-plugin/**/*.java]
ij_java_use_fq_class_names = false
+1 -1
View File
@@ -1,4 +1,4 @@
* text=auto eol=lf
* text=auto
*.sh text eol=lf
gradlew text eol=lf
+1 -1
View File
@@ -1,2 +1,2 @@
* @PaperMC/paper-maintainers
* @PaperMC/maintainers
/.github/CODEOWNERS @PaperMC/core-team
@@ -68,7 +68,7 @@ body:
value: |
Before submitting this issue, please ensure the following:
1. You are running the latest version of Paper from [our downloads page](https://papermc.io/downloads/paper).
1. You are running the latest version of Paper from [our downloads page](https://papermc.io/downloads).
2. You searched for and ensured there isn't already an open issue regarding this.
3. Your version of Minecraft is supported by Paper.
+1 -1
View File
@@ -41,4 +41,4 @@ body:
attributes:
value: |
Before submitting this feature request, please search our issue tracker to ensure your feature has not
already been requested.
alerady been requested.
@@ -35,7 +35,7 @@ body:
- type: textarea
attributes:
label: Server config files
description: We need bukkit.yml, spigot.yml, paper-global.yml, paper-world-defaults.yml and server.properties. If you use per-world Paper configs, make sure to include them. You can paste it below or use a paste site like https://paste.gg.
description: We need bukkit.yml, spigot.yml, paper.yml, and server.properties. You can paste it below or use a paste site like https://paste.gg.
value: |
```
Paste configs or paste.gg link here!
@@ -79,6 +79,6 @@ body:
value: |
Before submitting this issue, please ensure the following:
1. You are running the latest version of Paper from [our downloads page](https://papermc.io/downloads/paper).
1. You are running the latest version of Paper from [our downloads page](https://papermc.io/downloads).
2. You searched for and ensured there isn't already an open issue regarding this.
3. Your version of Minecraft is supported by Paper.
@@ -68,7 +68,7 @@ body:
value: |
Before submitting this issue, please ensure the following:
1. You are running the latest version of Paper from [our downloads page](https://papermc.io/downloads/paper).
1. You are running the latest version of Paper from [our downloads page](https://papermc.io/downloads).
2. Your version of Minecraft is supported by Paper.
If your server crash log contains `DO NOT REPORT THIS TO PAPER`, please ask in our
+18
View File
@@ -0,0 +1,18 @@
daysUntilStale: 60
daysUntilClose: 7
exemptLabels:
- "for: future"
- "type: bug"
- "status: accepted"
- "status: blocked"
- "status: in progress"
- "status: rebase required"
staleLabel: "resolution: stale"
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
closeComment: >
This issue has been automatically closed because it has not had activity in
a long time. If the issue still applies to the most recent supported
version, please open a new issue referencing this original issue.
+27 -83
View File
@@ -1,89 +1,33 @@
# Here lie dragons!
#
# This action either builds the server or
# builds a paperclip jar to be updated in the body
# of the PR relating to this action.
# Note that there is no artifact step in this script. We do not want Paperclip
# jars to be built for every push & PR; our CI handles pushes to branches, while
# PRs can themselves link to Paperclip jars if it is necessary. Official such
# PRs will take use of testing builds.
name: Build Paper
on:
push:
pull_request:
types:
- opened
- reopened
- synchronize
- labeled
on: [push, pull_request]
jobs:
build:
# Run on all label events (won't be duplicated) or all push events or on PR syncs not from the same repo
if: (github.event_name == 'pull_request' && github.event.action == 'labeled') || github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name
runs-on: ubuntu-latest
strategy:
matrix:
java: [17]
fail-fast: true
steps:
- uses: actions/checkout@v3
- name: JDK ${{ matrix.java }}
uses: actions/setup-java@v3.11.0
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: Configure Build
uses: actions/github-script@v6
id: determine
with:
script: |
const {owner, repo} = context.repo;
const event_name = "${{ github.event_name }}";
const event = ${{ toJSON(github.event) }};
const ref_type = "${{ github.ref_type }}";
const ref_name = "${{ github.ref_name }}";
const result = {
action: "build"
};
if (event_name === "push" && ref_type === "branch") {
const {data: pulls} = await github.rest.pulls.list({ owner, repo, head: `${owner}:${ref_name}`, state: "open" });
const pull = pulls.find((pr) => !!pr.labels.find((l) => l.name === "build-pr-jar"));
if (pull) {
result["pr"] = pull.number;
result["action"] = "paperclip";
core.notice(`This is a push action but to a branch with an open PR with the build paperclip label (${JSON.stringify(result)})`);
return result;
}
} else if (event_name === "pull_request" && event.pull_request.labels.find((l) => l.name === "build-pr-jar")) {
result["pr"] = event.pull_request.number;
result["action"] = "paperclip";
core.notice(`This is a pull request action with a build paperclip label (${JSON.stringify(result)})`);
return result;
}
core.notice("This will not build a paperclip jar");
return result;
- name: Apply Patches
run: |
git config --global user.email "no-reply@github.com"
git config --global user.name "Github Actions"
./gradlew applyPatches --stacktrace
- name: Build
run: ./gradlew build --stacktrace
- name: Create Paperclip Jar
if: fromJSON(steps.determine.outputs.result).action == 'paperclip'
run: ./gradlew createReobfPaperclipJar --stacktrace
- name: Upload Paperclip Jar
if: fromJSON(steps.determine.outputs.result).action == 'paperclip'
uses: actions/upload-artifact@v3
with:
name: paper-${{ fromJSON(steps.determine.outputs.result).pr }}
path: build/libs/paper-paperclip-*-reobf.jar
build:
# Only run on PRs if the source branch is on someone else's repo
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [17]
fail-fast: true
steps:
- uses: actions/checkout@v2.3.5
- name: JDK ${{ matrix.java }}
uses: actions/setup-java@v2.3.1
with:
java-version: ${{ matrix.java }}
cache: 'gradle'
distribution: 'temurin'
- name: Patch and build
run: |
git config --global user.email "no-reply@github.com"
git config --global user.name "Github Actions"
./gradlew applyPatches --stacktrace
./gradlew build --stacktrace
-14
View File
@@ -1,14 +0,0 @@
name: Close invalid PRs
on:
pull_request_target:
types: [ opened ]
jobs:
run:
if: ${{ github.repository != github.event.pull_request.head.repo.full_name && github.head_ref == 'master' }}
runs-on: ubuntu-latest
steps:
- uses: superbrothers/close-pull-request@v3
with:
comment: "Please do not open pull requests from the `master` branch, create a new branch instead."
-76
View File
@@ -1,76 +0,0 @@
# This workflow run on the completion of the
# build workflow but only does anything if the
# triggering workflow uploaded an artifact.
#
# Do note that it is then the trigger workflow that
# determines if this will update the PR text body. All
# this workflow does is check if an uploaded artifact
# exists and there is a PR tied to the previous workflow.
name: Comment on pull request
on:
workflow_run:
workflows: ['Build Paper']
types: [completed]
jobs:
pr_comment:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
# This snippet is public-domain, taken from
# https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml
# Modified extensively by Machine_Maker
script: |
async function updatePR(owner, repo, issue_number, purpose, body) {
const { data } = await github.rest.issues.get({ owner, repo, issue_number });
core.debug(JSON.stringify(data, null, 2));
const marker = `<!-- bot: ${purpose} -->`;
let new_body = data.body ? data.body.trim().split(marker)[0].trim() : "";
new_body += `\n${marker}\n---\n${body}`;
core.info(`Updating the text body of PR #${issue_number} in ${owner}/${repo}`);
await github.rest.issues.update({ owner, repo, issue_number, body: new_body });
}
const { owner, repo } = context.repo;
const run_id = ${{ github.event.workflow_run.id }};
const repo_id = ${{ github.event.repository.id }};
let pulls = [];
const event_type = "${{ github.event.workflow_run.event}}";
if (event_type === "push") { // if push, it's from the same repo which means `pull_requests` is populated
pulls = ${{ toJSON(github.event.workflow_run.pull_requests) }};
} else {
const pr_branch = "${{ github.event.workflow_run.head_branch }}";
const pr_sha = "${{ github.event.workflow_run.head_sha }}";
const pr_owner = "${{ github.event.workflow_run.head_repository.owner.login }}";
const { data } = await github.rest.pulls.list({ owner, repo, head: `${pr_owner}:${pr_branch}`, state: "open" });
core.debug(JSON.stringify(data, null, 2));
pulls = data.filter((pr) => pr.head.sha === pr_sha && pr.labels.find((l) => l.name === "build-pr-jar"));
}
if (!pulls.length) {
return core.notice("This workflow doesn't have any pull requests!");
} else if (pulls.length > 1) {
core.info(JSON.stringify(pulls, null, 2));
return core.error("Found multiple matching PRs");
}
const pull_request = pulls[0];
const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { owner, repo, run_id });
if (!artifacts.length) {
return core.info("Skipping comment due to no artifact found");
}
const artifact = artifacts.find((art) => art.name === `paper-${pull_request.number}`);
if (!artifact) {
return core.info("Skipping comment to no matching artifact found");
}
const link = `https://nightly.link/${owner}/${repo}/actions/artifacts/${artifact.id}.zip`;
const body = `Download the paperclip jar for this pull request: [${artifact.name}.zip](${link})`;
core.info(`Adding a link to ${link}`);
await updatePR(owner, repo, pull_request.number, "paperclip-pr-build", body);
-78
View File
@@ -1,78 +0,0 @@
name: Update Projects
on:
issues:
types:
- labeled
- unlabeled
- closed
- reopened
jobs:
bugs:
if: "github.event_name == 'issues' && contains(github.event.*.labels.*.name, 'type: bug')"
concurrency:
group: update-bugs-project-${{ github.event.issue.number }}
cancel-in-progress: true
runs-on: ubuntu-latest
steps:
- name: "authenticate"
id: "authenticate"
uses: "tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92" # v1
with:
app_id: "${{ secrets.PROJECTS_APP_ID }}"
installation_id: "36153445"
private_key: "${{ secrets.PROJECTS_PRIVATE_KEY }}"
- uses: PaperMC/update-projects-action@v0.2.0
name: Update open issue
if: github.event.issue.state == 'open'
with:
github-token: "${{ steps.authenticate.outputs.token }}"
project-url: https://github.com/orgs/PaperMC/projects/5/views/2
column-field: Status
label-to-column-map: |
{
"status: needs triage": "🕑 Needs Triage",
"status: accepted": "✅ Accepted",
"status: unlikely": "✅ Accepted",
"status: in progress": "Needs Work",
"status: rebase required": "Needs Work",
"status: blocked": "Needs Work",
"status: defer upstream": "Needs Work",
"status: input wanted": "Needs Work",
"we must go deeper": "Needs Work",
"has rough edges": "Needs Work",
"status: waiting on reporter": "⌛ Waiting for Reporter",
"resolution: works-as-intended": "Invalid",
"resolution: invalid": "Invalid",
"resolution: wontfix": "Invalid",
"resolution: duplicate": "Invalid",
"resolution: cannot reproduce": "Invalid",
"resolution: unsupported": "Invalid",
"resolution: incomplete": "Invalid",
"resolution: superseded": "Invalid"
}
- uses: PaperMC/update-projects-action@v0.2.0
name: Update closed issue
if: github.event.issue.state == 'closed'
with:
github-token: "${{ steps.authenticate.outputs.token }}"
project-url: https://github.com/orgs/PaperMC/projects/5/views/2
column-field: Status
clear-on-no-match: false
# include "status: needs triage" below to catch any closed issues without setting a resolution
label-to-column-map: |
{
"status: accepted": "Done",
"status: needs triage": "Invalid",
"resolution: works-as-intended": "Invalid",
"resolution: invalid": "Invalid",
"resolution: wontfix": "Invalid",
"resolution: duplicate": "Invalid",
"resolution: cannot reproduce": "Invalid",
"resolution: unsupported": "Invalid",
"resolution: incomplete": "Invalid",
"resolution: superseded": "Invalid"
}
-3
View File
@@ -53,9 +53,6 @@ work/ForgeFlower
.idea/
out/
# JetBrains Fleet
.fleet/
# Linux temp files
*~
+53 -83
View File
@@ -4,11 +4,11 @@ PaperMC 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 an Organization
## Use a Personal Fork and not Organization
Paper 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 them yourself.
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 Paper from
modifying it. This requires us to manually merge your PR, resulting in us
@@ -29,9 +29,9 @@ 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
- 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 11 or later installed. (Gradle will
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).
@@ -168,7 +168,7 @@ move it under the line of the patch you wish to modify;
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`.
1. Rebase with autosquash: `git rebase --autosquash -i 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;
@@ -184,7 +184,7 @@ These steps assume the `origin` remote is your fork of this repository and `upst
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 existing ones, in both the `Paper-Server` and `Paper-API` directories, ensure your newly-created patch is the last commit by either:
1. If your PR creates new patches instead of modifying exist ones, in both the `Paper-Server` and `Paper-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`.
@@ -229,44 +229,13 @@ entity.getWorld().explode(new BlockPosition(spawnLocation.getX(), spawnLocation.
// Paper end
```
We generally follow the usual Java style (aka. Oracle style), or what is programmed
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.
## Access Transformers
Sometimes, vanilla or CraftBukkit code already contains a field, method, or type you want to access
but the visibility is too low (e.g. a private field in an entity class). Paper can use access transformers
to change the visibility or remove the final modifier from fields, methods, and classes. Inside the `build-data/paper.at`
file, you can add ATs that are applied when you `./gradlew applyPatches`. You can read about the format of ATs
[here](https://mcforge.readthedocs.io/en/latest/advanced/accesstransformers/#access-modifiers).
### Important
ATs should be included in the patch file which requires them within the commit message. Do not commit any changes to the
`build-data/paper.at` file, just use it to initially change the visibility of members until you have finalized what you
need. Then, in the commit message for the patch which requires the ATs, add a header at the bottom of the commit message
before any co-authors. It should look like the following after you `./gradlew rebuildPatches`.
```
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Wed, 8 Jun 2022 22:20:16 -0700
Subject: [PATCH] Paper config files
This patch adds Paper configuration files.
Access transformers for this patch are below, but before the co-authors.
== AT ==
public org.spigotmc.SpigotWorldConfig getBoolean(Ljava/lang/String;Z)Z
public net.minecraft.world.level.NaturalSpawner SPAWNING_CATEGORIES
Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
diff --git a/build.gradle.kts b/build.gradle.kts
...
```
## Patch Notes
When submitting patches to Paper, we may ask you to add notes to the patch
@@ -340,55 +309,56 @@ 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
To use a configurable value in your patch, add a new entry in either the
`PaperConfig` or `PaperWorldConfig` classes. Use `PaperConfig` 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.
### PaperConfig example
```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
}
public static boolean saveEmptyScoreboardTeams = false;
private static void saveEmptyScoreboardTeams() {
// This is called automatically!
// The name also doesn't matter.
saveEmptyScoreboardTeams = getBoolean("settings.save-empty-scoreboard-teams", false);
}
```
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
Notice that the field is always public, but the setter is always private. This
is important to the way the configuration generation system works. To access
this value, reference it as you would any other static value:
```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;
if (!PaperConfig.saveEmptyScoreboardTeams) {
```
#### 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.
It is often preferred that you use the fully qualified name for the
configuration class when accessing it, like so:
`com.destroystokyo.paper.PaperConfig.valueHere`.
If this is not done, a developer for Paper might fix that for you before
merging, but it's always nice if you make it a habit where you only need 1-2
lines changed.
### PaperWorldConfig example
```java
public boolean useInhabitedTime = true;
private void useInhabitedTime() {
// This is called automatically!
// The name also doesn't matter.
useInhabitedTime = getBoolean("use-chunk-inhabited-timer", true);
}
```
Again, notice that the field is always public, but the setter is always private.
To access this value, you'll need an instance of the `net.minecraft.world.level.Level`
object:
```java
return this.level.paperConfig.useInhabitedTime ? this.inhabitedTime : 0;
```
## Testing API changes
@@ -432,7 +402,7 @@ 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
`./Paper-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.
[MiniMappingViewer] useful if you need to translate between Mojang and Spigot mapped names.
1. Open the file at `./build-data/dev-imports.txt` and add the name of your file to
the script. Follow the instructions there;
1. Re-patch the server `./gradlew applyPatches`;
@@ -457,16 +427,16 @@ file (`CONTRIBUTING.md`), the `LICENSE.md` file, and so forth.
### Patching and building is *really* slow, what can I do?
This only applies if you're running Windows. If you're running a prior Windows
release, either update to Windows 10/11 or move to macOS/Linux/BSD.
release, either update to Windows 10 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).
out of date, update your system with the
[Windows Update Assistant](https://www.microsoft.com/en-us/software-download/windows10).
To set up WSL 2, follow the information here:
<https://docs.microsoft.com/en-us/windows/wsl/install>
<https://docs.microsoft.com/en-us/windows/wsl/install-win10>
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
@@ -476,6 +446,6 @@ 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>
> <https://www.howtogeek.com/426749/how-to-access-your-linux-wsl-files-in-windows-10/>
[MappingViewer]: https://nms.screamingsandals.org/
[MiniMappingViewer]: https://minidigger.github.io/MiniMappingViewer/
+1 -15
View File
@@ -40,6 +40,7 @@ rickyboy320 <rickw320@hotmail.com>
DoNotSpamPls <7570108+DoNotSpamPls@users.noreply.github.com>
JRoy <joshroy126@gmail.com>
ysl3000 <yannicklamprecht@live.de>
kennytv <jahnke.nassim@gmail.com>
Machine_Maker <machine@machinemaker.me>
Ivan Pekov <ivan@mrivanplays.com>
Camotoy <20743703+Camotoy@users.noreply.github.com>
@@ -48,19 +49,4 @@ MeFisto94 <MeFisto94@users.noreply.github.com>
Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
LemonCaramel <admin@caramel.moe>
Noah van der Aa <ndvdaa@gmail.com>
Doc <nachito94@msn.com>
Nick Hensel <nickhensel25@icloud.com>
vytskalt <vytskalt@protonmail.com>
TheFruxz <cedricspitzer@outlook.de>
Kieran Wallbanks <kieran.wallbanks@gmail.com>
Denery <dorofeevij@gmail.com>
Jakubk15 <jakubk15@protonmail.com>
Redned <redned235@gmail.com>
Luke Chambers <consolelogluke@gmail.com>
Emily <emilia.lopezf.1999@gmail.com>
dawon <dawon@dawon.eu>
Ollie <69084614+olijeffers0n@users.noreply.github.com>
Oliwier Miodun <naczs@blueflow.pl>
aerulion <aerulion@gmail.com>
Lukas Planz <lukas.planz@web.de>
```
+5 -9
View File
@@ -8,6 +8,11 @@ java {
withJavadocJar()
}
repositories {
mavenCentral()
maven("https://libraries.minecraft.net")
}
dependencies {
implementation(project(":paper-api"))
api("com.mojang:brigadier:1.0.18")
@@ -25,12 +30,3 @@ configure<PublishingExtension> {
from(components["java"])
}
}
val scanJar = tasks.register("scanJarForBadCalls", io.papermc.paperweight.tasks.ScanJarForBadCalls::class) {
badAnnotations.add("Lio/papermc/paper/annotation/DoNotUse;")
jarToScan.set(tasks.jar.flatMap { it.archiveFile })
classpath.from(configurations.compileClasspath)
}
tasks.check {
dependsOn(scanJar)
}
@@ -5,10 +5,5 @@ import com.mojang.brigadier.suggestion.SuggestionProvider;
import java.util.function.Predicate;
/**
* Brigadier {@link Command}, {@link SuggestionProvider}, and permission checker for Bukkit {@link Command}s.
*
* @param <S> command source type
*/
public interface BukkitBrigadierCommand <S extends BukkitBrigadierCommandSource> extends Command<S>, Predicate<S>, SuggestionProvider<S> {
}
@@ -3,33 +3,33 @@ package com.destroystokyo.paper.event.brigadier;
import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource;
import com.mojang.brigadier.tree.RootCommandNode;
import org.bukkit.Bukkit;
import org.bukkit.Warning;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Fired any time a Brigadier RootCommandNode is generated for a player to inform the client of commands.
* You may manipulate this CommandNode to change what the client sees.
*
* <p>This event may fire on login, world change, and permission rebuilds, by plugin request, and potentially future means.</p>
* This event may fire on login, world change, and permission rebuilds, by plugin request, and potentially future means.
*
* <p>This event will fire before {@link org.bukkit.event.player.PlayerCommandSendEvent}, so no filtering has been done by
* other plugins yet.</p>
* This event will fire before {@link org.bukkit.event.player.PlayerCommandSendEvent}, so no filtering has been done by
* other plugins yet.
*
* <p>WARNING: This event will potentially (and most likely) fire twice! Once for Async, and once again for Sync.
* WARNING: This event will potentially (and most likely) fire twice! Once for Async, and once again for Sync.
* It is important that you check event.isAsynchronous() and event.hasFiredAsync() to ensure you only act once.
* If for some reason we are unable to send this asynchronously in the future, only the sync method will fire.</p>
* If for some reason we are unable to send this asynchronously in the future, only the sync method will fire.
*
* <p>Your logic should look like this:
* {@code if (event.isAsynchronous() || !event.hasFiredAsync()) { // do stuff }}</p>
* Your logic should look like this:
* if (event.isAsynchronous() || !event.hasFiredAsync()) { do stuff }
*
* <p>If your logic is not safe to run asynchronously, only react to the synchronous version.</p>
*
* <p>This is a draft/experimental API and is subject to change.</p>
* If your logic is not safe to run asynchronously, only react to the synchronous version.
* @deprecated Draft API - Subject to change until confirmed solves desired use cases
*/
@ApiStatus.Experimental
@Deprecated
@Warning(false)
public class AsyncPlayerSendCommandsEvent <S extends BukkitBrigadierCommandSource> extends PlayerEvent {
private static final HandlerList handlers = new HandlerList();
@@ -43,18 +43,14 @@ public class AsyncPlayerSendCommandsEvent <S extends BukkitBrigadierCommandSourc
}
/**
* Gets the full Root Command Node being sent to the client, which is mutable.
*
* @return the root command node
* @return The full Root Command Node being sent to the client, which is mutable.
*/
public RootCommandNode<S> getCommandNode() {
return node;
}
/**
* Gets if this event has already fired asynchronously.
*
* @return whether this event has already fired asynchronously
* @return If this event has already fired asynchronously.
*/
public boolean hasFiredAsync() {
return hasFiredAsync;
@@ -10,9 +10,8 @@ import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called when sending {@link Suggestions} to the client. Will be called asynchronously if a plugin
* marks the {@link com.destroystokyo.paper.event.server.AsyncTabCompleteEvent} event handled asynchronously,
* otherwise called synchronously.
* Called when sending Suggestions to the client. Will be called asynchronously if a plugin
* marks the AsyncTabComplete event handled asynchronously, otherwise called synchronously.
*/
public class AsyncPlayerSendSuggestionsEvent extends PlayerEvent implements Cancellable {
@@ -29,27 +28,21 @@ public class AsyncPlayerSendSuggestionsEvent extends PlayerEvent implements Canc
}
/**
* Gets the input buffer sent to request these suggestions.
*
* @return the input buffer
* @return The input buffer sent to request these suggestions
*/
public String getBuffer() {
return buffer;
}
/**
* Gets the suggestions to be sent to client.
*
* @return the suggestions
* @return The suggestions being sent to client
*/
public Suggestions getSuggestions() {
return suggestions;
}
/**
* Sets the suggestions to be sent to client.
*
* @param suggestions suggestions
* @param suggestions The suggestions to be sent to client if need to change them
*/
public void setSuggestions(Suggestions suggestions) {
this.suggestions = suggestions;
@@ -64,7 +57,7 @@ public class AsyncPlayerSendSuggestionsEvent extends PlayerEvent implements Canc
}
/**
* Cancels sending suggestions to the client.
* Cancels sending suggestions to the client
* {@inheritDoc}
*/
@Override
@@ -5,24 +5,26 @@ import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource;
import com.mojang.brigadier.tree.ArgumentCommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import org.bukkit.Warning;
import org.bukkit.command.Command;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.server.ServerEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Fired anytime the server synchronizes Bukkit commands to Brigadier.
* Fired anytime the server synchronizes Bukkit CommandMap to Brigadier.
*
* <p>Allows a plugin to control the command node structure for its commands.
* This is done at Plugin Enable time after commands have been registered, but may also
* run at a later point in the server lifetime due to plugins, a server reload, etc.</p>
* Allows a plugin to control the Literal and Argument nodes for this command to be
* sent to the client.
* This is done at Plugin Enable time after commands have been registered, but some
* plugins may use reflection to retrigger this rebuild during runtime.
*
* <p>This is a draft/experimental API and is subject to change.</p>
* @deprecated Draft API - Subject to change until confirmed solves desired use cases
*/
@ApiStatus.Experimental
public class CommandRegisteredEvent<S extends BukkitBrigadierCommandSource> extends ServerEvent implements Cancellable {
@Deprecated
@Warning(false)
public class CommandRegisteredEvent <S extends BukkitBrigadierCommandSource> extends ServerEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final String commandLabel;
@@ -31,7 +33,6 @@ public class CommandRegisteredEvent<S extends BukkitBrigadierCommandSource> exte
private final RootCommandNode<S> root;
private final ArgumentCommandNode<S, String> defaultArgs;
private LiteralCommandNode<S> literal;
private boolean rawCommand = false;
private boolean cancelled = false;
public CommandRegisteredEvent(String commandLabel, BukkitBrigadierCommand<S> brigadierCommand, Command command, RootCommandNode<S> root, LiteralCommandNode<S> literal, ArgumentCommandNode<S, String> defaultArgs) {
@@ -44,99 +45,55 @@ public class CommandRegisteredEvent<S extends BukkitBrigadierCommandSource> exte
}
/**
* Gets the command label of the {@link Command} being registered.
*
* @return the command label
* @return The command name being registered
*/
public String getCommandLabel() {
return this.commandLabel;
return commandLabel;
}
/**
* Gets the {@link BukkitBrigadierCommand} for the {@link Command} being registered. This can be used
* as the {@link com.mojang.brigadier.Command command executor} or
* {@link com.mojang.brigadier.suggestion.SuggestionProvider} of a {@link com.mojang.brigadier.tree.CommandNode}
* to delegate to the {@link Command} being registered.
*
* @return the {@link BukkitBrigadierCommand}
* @return The Bukkit API Brigadier Wrapped Command Object to handle executions and suggestions
*/
public BukkitBrigadierCommand<S> getBrigadierCommand() {
return this.brigadierCommand;
return brigadierCommand;
}
/**
* Gets the {@link Command} being registered.
*
* @return the {@link Command}
*/
public Command getCommand() {
return this.command;
return command;
}
/**
* Gets the {@link RootCommandNode} which is being registered to.
*
* @return the {@link RootCommandNode}
* @return Gets the root command node being used to register a command to.
*/
public RootCommandNode<S> getRoot() {
return this.root;
return root;
}
/**
* Gets the Bukkit APIs default arguments node (greedy string), for if
* you wish to reuse it.
*
* @return default arguments node
* Returns the Bukkit API's default handling of Arguments, if you wish to reuse it.
* @return
*/
public ArgumentCommandNode<S, String> getDefaultArgs() {
return this.defaultArgs;
return defaultArgs;
}
/**
* Gets the {@link LiteralCommandNode} to be registered for the {@link Command}.
*
* @return the {@link LiteralCommandNode}
* Returns the Bukkit API's default literal for this command, including the {@link #getDefaultArgs()} as a child already.
* @return
*/
public LiteralCommandNode<S> getLiteral() {
return this.literal;
return literal;
}
/**
* Sets the {@link LiteralCommandNode} used to register this command. The default literal is mutable, so
* this is primarily if you want to completely replace the object.
*
* @param literal new node
* Changes the literal used to register this command. The previous literable is mutable, so this is primarily if
* you want to completely replace the object.
* @param literal
*/
public void setLiteral(LiteralCommandNode<S> literal) {
this.literal = literal;
}
/**
* Gets whether this command should is treated as "raw".
*
* @see #setRawCommand(boolean)
* @return whether this command is treated as "raw"
*/
public boolean isRawCommand() {
return this.rawCommand;
}
/**
* Sets whether this command should be treated as "raw".
*
* <p>A "raw" command will only use the node provided by this event for
* sending the command tree to the client. For execution purposes, the default
* greedy string execution of a standard Bukkit {@link Command} is used.</p>
*
* <p>On older versions of Paper, this was the default and only behavior of this
* event.</p>
*
* @param rawCommand whether this command should be treated as "raw"
*/
public void setRawCommand(final boolean rawCommand) {
this.rawCommand = rawCommand;
}
/**
* {@inheritDoc}
*/
+22 -21
View File
@@ -1,59 +1,60 @@
Paper [![Paper Build Status](https://img.shields.io/github/actions/workflow/status/PaperMC/Paper/build.yml?branch=master)](https://github.com/PaperMC/Paper/actions)
Paper [![Paper Build Status](https://img.shields.io/github/workflow/status/PaperMC/Paper/Build%20Paper/master)](https://github.com/PaperMC/Paper/actions)
[![Discord](https://img.shields.io/discord/289587909051416579.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/papermc)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/papermc?label=GitHub%20Sponsors)](https://github.com/sponsors/PaperMC)
[![Open Collective](https://img.shields.io/opencollective/all/papermc?label=OpenCollective%20Sponsors)](https://opencollective.com/papermc)
===========
The most widely used, high-performance Minecraft server that aims to fix gameplay and mechanics inconsistencies.
High performance Spigot fork that aims to fix gameplay and mechanics inconsistencies.
**Support and Project Discussion:**
- [Our forums](https://forums.papermc.io/), [Discord](https://discord.gg/papermc), or [IRC](https://webchat.esper.net/?channels=paper)
- [IRC](https://webchat.esper.net/?channels=paper) or [Discord](https://discord.gg/papermc)
How To (Server Admins)
------
Paperclip is a jar file that you can download and run just like a normal jar file.
Download Paper from our [downloads page](https://papermc.io/downloads/paper).
Download Paper from our [downloads page](https://papermc.io/downloads).
Run the Paperclip jar directly from your server. Just like old times
* Documentation on using Paper: [docs.papermc.io](https://docs.papermc.io)
* For a sneak peek at upcoming features, [see here](https://github.com/PaperMC/Paper/projects)
* Documentation on using Paper: [paper.readthedocs.io](https://paper.readthedocs.io/)
* For a sneak peek on upcoming features, [see here](https://github.com/PaperMC/Paper/projects)
How To (Plugin Developers)
------
* See our API patches [here](patches/api)
* See upcoming, pending, and recently added API [here](https://github.com/orgs/PaperMC/projects/2/views/4)
* Paper API javadocs here: [papermc.io/javadocs](https://papermc.io/javadocs/)
#### Repository (for paper-api)
##### Maven
* See our API patches [here](patches/api)
* See upcoming, pending, and recently added API [here](https://github.com/PaperMC/Paper/projects/6)
* Paper API javadocs here: [papermc.io/javadocs](https://papermc.io/javadocs/)
* Maven Repo (for paper-api):
```xml
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
```
* Artifact Information:
```xml
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<version>1.18.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
```
##### Gradle
```
**Or alternatively, with Gradle:**
* Repository:
```kotlin
repositories {
maven {
url = uri("https://repo.papermc.io/repository/maven-public/")
url = uri("https://papermc.io/repo/repository/maven-public/")
}
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT")
compileOnly("io.papermc.paper:paper-api:1.18.1-R0.1-SNAPSHOT")
}
java {
@@ -75,9 +76,9 @@ See [Contributing](CONTRIBUTING.md)
Support Us
------
First of all, thank you for considering helping out, we really appreciate that!
First of all, thank you for considering helping out, we really appreciate that!
PaperMC has various recurring expenses, mostly related to infrastructure. Paper uses [Open Collective](https://opencollective.com/) via the [Open Source Collective fiscal host](https://opencollective.com/opensource) to manage expenses. Open Collective allows us to be extremely transparent, so you can always see how your donations are used. You can read more about financially supporting PaperMC [on our website](https://papermc.io/sponsors).
PaperMC has various recurring expenses, mostly related to infrastructure. Paper uses [Open Collective](https://opencollective.com/) via the [Open Source Collective fiscal host](https://opencollective.com/opensource) to manage expenses. Open Collective allows us to be extremely transparent, so you can always see how your donations are used. You can read more about finanically supporting PaperMC [on our website](https://papermc.io/sponsors).
You can find our collective [here](https://opencollective.com/papermc), or you can donate via GitHub Sponsors [here](https://github.com/sponsors/PaperMC), which will also go towards the collective.
+282 -3
View File
@@ -13,6 +13,285 @@ public net.minecraft.server.MinecraftServer doRunTask(Lnet/minecraft/server/Tick
public net.minecraft.world.level.dimension.end.EndDragonFight findExitPortal()Lnet/minecraft/world/level/block/state/pattern/BlockPattern$BlockPatternMatch;
public net.minecraft.nbt.TagParser readArrayTag()Lnet/minecraft/nbt/Tag;
# TODO 1.20 remapSpigotAt.at doesn't remap the return type for this method for some reason
public net/minecraft/world/entity/Display$TextDisplay getText()Lnet/minecraft/network/chat/Component;
public net/minecraft/world/entity/Display$BlockDisplay getBlockState()Lnet/minecraft/world/level/block/state/BlockState;
# Paper config files
public org.spigotmc.SpigotWorldConfig getBoolean(Ljava/lang/String;Z)Z
public org.spigotmc.SpigotWorldConfig getDouble(Ljava/lang/String;)D
public org.spigotmc.SpigotWorldConfig getDouble(Ljava/lang/String;D)D
public org.spigotmc.SpigotWorldConfig getInt(Ljava/lang/String;)I
public org.spigotmc.SpigotWorldConfig getInt(Ljava/lang/String;I)I
public org.spigotmc.SpigotWorldConfig getList(Ljava/lang/String;Ljava/lang/Object;)Ljava/util/List;
public org.spigotmc.SpigotWorldConfig getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
# MC Utils
public net.minecraft.server.level.ServerChunkCache mainThread
public net.minecraft.server.level.ServerLevel chunkSource
public org.bukkit.craftbukkit.inventory.CraftItemStack handle
# Add PlayerInitialSpawnEvent
public net.minecraft.world.entity.Entity setRot(FF)V
# Add PlayerUseUnknownEntityEvent
public net.minecraft.network.protocol.game.ServerboundInteractPacket$ActionType
# Configurable RCON IP address
public net.minecraft.server.dedicated.Settings getStringRaw(Ljava/lang/String;)Ljava/lang/String;
# Mob Spawner API Enhancements
public net.minecraft.world.level.BaseSpawner isNearPlayer(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)Z
public net.minecraft.world.level.BaseSpawner delay(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)V
# LootTable API
public org.bukkit.craftbukkit.block.CraftBlockEntityState getTileEntity()Lnet/minecraft/world/level/block/entity/BlockEntity;
public org.bukkit.craftbukkit.block.CraftLootable setLootTable(Lorg/bukkit/loot/LootTable;J)V
public org.bukkit.craftbukkit.entity.CraftMinecartContainer setLootTable(Lorg/bukkit/loot/LootTable;J)V
# Firework API
public net.minecraft.world.entity.projectile.FireworkRocketEntity attachedToEntity
# Add option to make parrots stay
public net.minecraft.world.entity.player.Player removeEntitiesOnShoulder()V
# LivingEntity setkiller
public net.minecraft.world.entity.LivingEntity lastHurtByPlayerTime
# SkeletonHorse Additions
public net.minecraft.world.entity.animal.horse.SkeletonHorse trapTime
# Fix client rendering skulls
public net.minecraft.world.item.ItemStack tag
# Async chunk io
public net.minecraft.server.level.ChunkMap structureManager
public net.minecraft.server.level.ChunkMap overworldDataStorage
public net.minecraft.server.level.ChunkMap getUpdatingChunkIfPresent(J)Lnet/minecraft/server/level/ChunkHolder;
public net.minecraft.server.level.ChunkMap getVisibleChunkIfPresent(J)Lnet/minecraft/server/level/ChunkHolder;
public net.minecraft.server.level.ServerChunkCache mainThreadProcessor
public-f net.minecraft.world.level.chunk.storage.RegionFileStorage
public net.minecraft.world.level.chunk.storage.RegionFileStorage getFile(Lnet/minecraft/world/level/ChunkPos;Z)Lnet/minecraft/world/level/chunk/storage/RegionFile;
public net.minecraft.world.level.chunk.storage.SectionStorage dirty
public net.minecraft.util.thread.BlockableEventLoop runAllTasks()V
public net.minecraft.server.level.ChunkMap getPoiManager()Lnet/minecraft/world/entity/ai/village/poi/PoiManager;
# Improve death events
public net.minecraft.world.entity.LivingEntity getDeathSound()Lnet/minecraft/sounds/SoundEvent;
public net.minecraft.world.entity.LivingEntity getSoundVolume()F
public net.minecraft.world.entity.Mob handItems
public net.minecraft.world.entity.Mob armorItems
# Add sun related api
public net.minecraft.world.entity.Mob isSunBurnTick()Z
# Turtle API
public net.minecraft.world.entity.animal.Turtle getHomePos()Lnet/minecraft/core/BlockPos;
public net.minecraft.world.entity.animal.Turtle setHasEgg(Z)V
public net.minecraft.world.entity.animal.Turtle isGoingHome()Z
public net.minecraft.world.entity.animal.Turtle setGoingHome(Z)V
public net.minecraft.world.entity.animal.Turtle isTravelling()Z
public net.minecraft.world.entity.animal.Turtle setTravelling(Z)V
# Call player spectator target event
public net.minecraft.server.network.ServerGamePacketListenerImpl updateBookPages(Ljava/util/List;Ljava/util/function/UnaryOperator;Lnet/minecraft/world/item/ItemStack;ILnet/minecraft/world/item/ItemStack;)V
# Improve Server THread Pool
public net.minecraft.Util onThreadException(Ljava/lang/Thread;Ljava/lang/Throwable;)V
# Add more zombie API
public net.minecraft.world.entity.monster.Zombie isSunSensitive()Z
# Add PlayerConnectionCloseEvent
public net.minecraft.server.network.ServerLoginPacketListenerImpl$State
public net.minecraft.server.network.ServerLoginPacketListenerImpl state
public net.minecraft.server.network.ServerLoginPacketListenerImpl gameProfile
# Entity Activation Range 2.0
public net.minecraft.world.entity.Entity isInsidePortal
public net.minecraft.world.entity.Mob leashHolder
public net.minecraft.world.entity.LivingEntity jumping
# No-Tick view distance
public net.minecraft.server.level.ChunkHolder broadcast(Lnet/minecraft/network/protocol/Packet;Z)V
public net.minecraft.server.level.ChunkMap setViewDistance(I)V
public net.minecraft.server.level.ChunkMap readChunk(Lnet/minecraft/world/level/ChunkPos;)Lnet/minecraft/nbt/CompoundTag;
public net.minecraft.server.level.ChunkMap playerLoadedChunk(Lnet/minecraft/server/level/ServerPlayer;[Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/world/level/chunk/LevelChunk;)V
public net.minecraft.server.level.ChunkMap mainThreadMailbox # todo one of these doesn't belong here
# Optimise TickListServer
public net.minecraft.world.level.ServerTickList saveTickList(Ljava/util/function/Function;Ljava/lang/Iterable;J)Lnet/minecraft/nbt/ListTag;
public net.minecraft.world.level.chunk.storage.EntityStorage level
# Don't move existing players to world spawn
public net.minecraft.server.level.ServerPlayer fudgeSpawnLocation(Lnet/minecraft/server/level/ServerLevel;)V
# Implement Player Client Options API
public net.minecraft.world.entity.player.Player DATA_PLAYER_MODE_CUSTOMISATION
# Fix Longstanding Broken behavior
public net.minecraft.server.level.ChunkMap addEntity(Lnet/minecraft/world/entity/Entity;)V
# Load Chunks for Login Async
public net.minecraft.server.level.ServerChunkCache runDistanceManagerUpdates()Z
public net.minecraft.server.level.ServerChunkCache$MainThreadExecutor # todo doesn't belong here but oh well
# Implement MobGoalApi
public net.minecraft.world.entity.ai.goal.GoalSelector availableGoals
# Add villager reputation API
public net.minecraft.world.entity.ai.gossip.GossipContainer$EntityGossips
public net.minecraft.world.entity.ai.gossip.GossipContainer$EntityGossips <init>()V
# Add entity liquid API
public net.minecraft.world.entity.Entity isInRain()Z
public net.minecraft.world.entity.Entity isInBubbleColumn()Z
# Allow delegation to vanilla chunk gen
public org.bukkit.craftbukkit.generator.CustomChunkGenerator delegate
# Optimize redstone algorithm
public net.minecraft.world.level.block.RedStoneWireBlock shouldSignal
# Add more Evoker API
public net.minecraft.world.entity.monster.Evoker setWololoTarget(Lnet/minecraft/world/entity/animal/Sheep;)V
public net.minecraft.world.entity.monster.Evoker getWololoTarget()Lnet/minecraft/world/entity/animal/Sheep;
# More lightning API
public net.minecraft.world.entity.LightningBolt life
public net.minecraft.world.entity.LightningBolt flashes
# Configurable door breaking difficulty
public net.minecraft.world.entity.monster.Vindicator DOOR_BREAKING_PREDICATE
public net.minecraft.world.entity.monster.Zombie DOOR_BREAKING_PREDICATE
# Optimize sending packets to nearby locations (sounds/effects)
public net.minecraft.server.level.ServerLevel players
# Item Rarity API
public net.minecraft.world.item.Item rarity
# More Enchantment API
public net.minecraft.world.item.enchantment.Enchantment slots
# Fix and optimise world force upgrading
public net.minecraft.util.worldupdate.WorldUpgrader REGEX
# More Lidded Block API
public net.minecraft.world.level.block.entity.EnderChestBlockEntity openersCounter
# Improve EntityShootBowEvent
public net.minecraft.world.entity.projectile.AbstractArrow getPickupItem()Lnet.minecraft.world.item.ItemStack;
# Implement Expanded ArmorStand API
public net.minecraft.world.entity.decoration.ArmorStand isDisabled(Lnet/minecraft/world/entity/EquipmentSlot;)Z
# Chunk debug command
public net.minecraft.server.level.ChunkMap entitiesInLevel
public net.minecraft.server.level.ServerLevel players
# Chunk priority urgency system
public net.minecraft.server.level.ChunkMap$ChunkDistanceManager
# Here's Johnny
public net.minecraft.world.entity.monster.Vindicator isJohnny
# Chunk debug command
public net.minecraft.server.level.Ticket createdTick
public net.minecraft.server.level.ServerChunkCache CHUNK_STATUSES
public net.minecraft.server.level.ChunkHolder pos
# Incremental chunk saving
public net.minecraft.world.level.entity.PersistentEntitySectionManager storeChunkSections(JLjava/util/function/Consumer;)Z
# Mending XP API
public net.minecraft.world.entity.ExperienceOrb durabilityToXp(I)I
public net.minecraft.world.entity.ExperienceOrb xpToDurability(I)I
# Implement an API for CanPlaceOn and CanDestroy NBT values
public net.minecraft.commands.arguments.blocks.BlockStateParser id
# Villager Restock API
public net.minecraft.world.entity.npc.Villager numberOfRestocksToday
# Mob Pathfinding API
public net.minecraft.world.entity.ai.navigation.PathNavigation pathFinder
public net.minecraft.world.level.pathfinder.PathFinder nodeEvaluator
public net.minecraft.world.level.pathfinder.Path nodes
# Add more Witch API
public net.minecraft.world.entity.monster.Witch usingTime
# PlayerDeathEvent#getItemsToKeep
public net.minecraft.world.entity.player.Inventory compartments
# Missing Entity Behavior API
public net.minecraft.world.entity.animal.Fox isDefending()Z
public net.minecraft.world.entity.animal.Fox setDefending(Z)V
public net.minecraft.world.entity.animal.Fox isFaceplanted()Z
public net.minecraft.world.entity.animal.Fox setFaceplanted(Z)V
# Cook speed multipler API
public net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity recipeType
# Add methods to get translation keys
public org.bukkit.craftbukkit.inventory.CraftMetaFirework
public org.bukkit.craftbukkit.inventory.CraftMetaFirework getNBT(Lorg/bukkit/FireworkEffect$Type;)I
# Vanilla command permission fixes
public-f com.mojang.brigadier.tree.CommandNode requirement
# Block Enderpearl Travel Exploit
public net.minecraft.world.entity.projectile.Projectile cachedOwner
public net.minecraft.world.entity.projectile.Projectile ownerUUID
# Add missing display slots
public org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations
public org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations toBukkitSlot(I)Lorg/bukkit/scoreboard/DisplaySlot;
public org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations fromBukkitSlot(Lorg/bukkit/scoreboard/DisplaySlot;)I
# Add methods to find targets for lightning strikes
public net.minecraft.server.level.ServerLevel findLightningRod(Lnet/minecraft/core/BlockPos;)Ljava/util/Optional;
# Improve CraftBlockStates
public net.minecraft.world.level.block.entity.BlockEntityType validBlocks
# Default entity attributes
public net.minecraft.world.entity.ai.attributes.AttributeSupplier instances
# Add ItemFactory#getSpawnEgg API
public net.minecraft.world.item.SpawnEggItem BY_ID
# Zombie API - breaking doors
public net.minecraft.world.entity.monster.Zombie supportsBreakDoorGoal()Z
# Add Material#hasCollision
public net.minecraft.world.level.block.state.BlockBehaviour hasCollision
# add per world spawn limits
public net.minecraft.world.level.NaturalSpawner SPAWNING_CATEGORIES
# Optimize isValidLocation
public net.minecraft.world.level.chunk.LevelChunkSection states
# Player.setPlayerProfile API
public-f net.minecraft.world.entity.player.Player gameProfile
# Generator Settings
public net.minecraft.world.level.levelgen.SurfaceRules$Condition
public net.minecraft.world.level.levelgen.SurfaceRules$Context
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockX
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockY
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockZ
public net.minecraft.world.level.levelgen.SurfaceRules$Context context
public net.minecraft.world.level.levelgen.SurfaceRules$Context system
public net.minecraft.world.level.levelgen.SurfaceRules$LazyYCondition
public net.minecraft.world.level.levelgen.SurfaceRules$LazyCondition
public net.minecraft.world.level.levelgen.SurfaceRules$VerticalGradientConditionSource
public net.minecraft.world.level.levelgen.SurfaceRules$SurfaceRule
public net.minecraft.world.level.levelgen.SurfaceSystem getOrCreateRandomFactory(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/world/level/levelgen/PositionalRandomFactory;
# Fix removing recipes
public net.minecraft.world.item.crafting.RecipeManager byName
# Entity powdered snow API
public net.minecraft.world.entity.monster.Skeleton inPowderSnowTime
# Add health methods for item entities
public net.minecraft.world.entity.item.ItemEntity health
+5 -4
View File
@@ -14,15 +14,16 @@ tiny 2 0 mojang+yarn spigot
# CraftBukkit changes type
c net/minecraft/server/level/ServerLevel net/minecraft/server/level/WorldServer
f Lnet/minecraft/world/level/storage/PrimaryLevelData; serverLevelData K
f Lnet/minecraft/world/level/storage/PrimaryLevelData; serverLevelData N
c net/minecraft/world/level/chunk/LevelChunk net/minecraft/world/level/chunk/Chunk
f Lnet/minecraft/server/level/ServerLevel; level r
f Lnet/minecraft/server/level/ServerLevel; level q
# See mappings-patch.tiny
c net/minecraft/server/level/ChunkMap net/minecraft/server/level/PlayerChunkMap
f Lnet/minecraft/server/level/ChunkMap$ChunkDistanceManager; distanceManager F
f Lnet/minecraft/server/level/ChunkMap$ChunkDistanceManager; distanceManager D
# 1.18 todo: verify paper still changes this
# Paper changes type
c net/minecraft/core/MappedRegistry net/minecraft/core/RegistryMaterials
f Lit/unimi/dsi/fastutil/objects/Reference2IntOpenHashMap; toId e
f Lit/unimi/dsi/fastutil/objects/Reference2IntOpenHashMap; toId bA
+20 -54
View File
@@ -1,11 +1,8 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
plugins {
java
`maven-publish`
id("com.github.johnrengelman.shadow") version "8.1.1" apply false
id("io.papermc.paperweight.core") version "1.5.5"
id("com.github.johnrengelman.shadow") version "7.1.1" apply false
id("io.papermc.paperweight.core") version "1.3.3"
}
allprojects {
@@ -19,8 +16,6 @@ allprojects {
}
}
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
subprojects {
tasks.withType<JavaCompile> {
options.encoding = Charsets.UTF_8.name()
@@ -32,53 +27,38 @@ subprojects {
tasks.withType<ProcessResources> {
filteringCharset = Charsets.UTF_8.name()
}
tasks.withType<Test> {
testLogging {
showStackTraces = true
exceptionFormat = TestExceptionFormat.FULL
events(TestLogEvent.STANDARD_OUT)
}
if (name == "Paper-MojangAPI") {
return@subprojects
}
repositories {
mavenCentral()
maven(paperMavenPublicUrl)
maven("https://papermc.io/repo/repository/maven-public/")
}
}
val spigotDecompiler: Configuration by configurations.creating
repositories {
mavenCentral()
maven(paperMavenPublicUrl) {
content {
onlyForConfigurations(
configurations.paperclip.name,
spigotDecompiler.name,
)
}
maven("https://papermc.io/repo/repository/maven-public/") {
content { onlyForConfigurations("paperclip") }
}
}
dependencies {
paramMappings("net.fabricmc:yarn:1.20.1+build.1:mergedv2")
remapper("net.fabricmc:tiny-remapper:0.8.6:fat")
decompiler("net.minecraftforge:forgeflower:2.0.627.2")
spigotDecompiler("io.papermc:patched-spigot-fernflower:0.1+build.6")
paperclip("io.papermc:paperclip:3.0.3")
paramMappings("net.fabricmc:yarn:1.18.1+build.1:mergedv2")
remapper("net.fabricmc:tiny-remapper:0.7.0:fat")
decompiler("net.minecraftforge:forgeflower:1.5.498.22")
paperclip("io.papermc:paperclip:3.0.2")
}
paperweight {
minecraftVersion.set(providers.gradleProperty("mcVersion"))
serverProject.set(project(":paper-server"))
paramMappingsRepo.set(paperMavenPublicUrl)
remapRepo.set(paperMavenPublicUrl)
decompileRepo.set(paperMavenPublicUrl)
craftBukkit {
fernFlowerJar.set(layout.file(spigotDecompiler.elements.map { it.single().asFile }))
}
paramMappingsRepo.set("https://maven.fabricmc.net/")
remapRepo.set("https://maven.fabricmc.net/")
decompileRepo.set("https://files.minecraftforge.net/maven/")
paper {
spigotApiPatchDir.set(layout.projectDirectory.dir("patches/api"))
@@ -106,12 +86,14 @@ tasks.generateDevelopmentBundle {
mojangApiCoordinates.set("io.papermc.paper:paper-mojangapi")
libraryRepositories.addAll(
"https://repo.maven.apache.org/maven2/",
paperMavenPublicUrl,
"https://libraries.minecraft.net/",
"https://papermc.io/repo/repository/maven-public/",
"https://maven.fabricmc.net/",
)
}
publishing {
if (project.providers.gradleProperty("publishDevBundle").isPresent) {
if (project.providers.gradleProperty("publishDevBundle").forUseAtConfigurationTime().isPresent) {
publications.create<MavenPublication>("devBundle") {
artifact(tasks.generateDevelopmentBundle) {
artifactId = "dev-bundle"
@@ -123,7 +105,7 @@ publishing {
allprojects {
publishing {
repositories {
maven("https://repo.papermc.io/repository/maven-snapshots/") {
maven("https://papermc.io/repo/repository/maven-snapshots/") {
name = "paperSnapshots"
credentials(PasswordCredentials::class)
}
@@ -131,24 +113,8 @@ allprojects {
}
}
tasks.collectAtsFromPatches {
// Uncomment while updating for a new Minecraft version
// extraPatchDir.set(layout.projectDirectory.dir("patches/unapplied/server"))
}
// Uncomment while updating for a new Minecraft version
// tasks.withType<io.papermc.paperweight.tasks.RebuildGitPatches> {
// filterPatches.set(false)
// }
tasks.register("printMinecraftVersion") {
doLast {
println(providers.gradleProperty("mcVersion").get().trim())
}
}
tasks.register("printPaperVersion") {
doLast {
println(project.version)
}
}
+3 -2
View File
@@ -1,6 +1,7 @@
group=io.papermc.paper
version=1.20.1-R0.1-SNAPSHOT
mcVersion=1.20.1
version=1.18.1-R0.1-SNAPSHOT
mcVersion=1.18.1
org.gradle.caching=true
org.gradle.parallel=true
Binary file not shown.
+1 -3
View File
@@ -1,7 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+8 -23
View File
@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/master/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/.
@@ -80,11 +80,13 @@ do
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && 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
@@ -131,29 +133,22 @@ location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
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
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
@@ -198,10 +193,6 @@ if "$cygwin" || "$msys" ; then
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
@@ -214,12 +205,6 @@ set -- \
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.
Vendored
+6 -9
View File
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,8 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -41,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -76,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
if "%ERRORLEVEL%"=="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%
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
+1 -1
View File
@@ -672,4 +672,4 @@ program into proprietary programs. If your program is a subroutine
library, you may consider it more useful to permit linking proprietary
applications with the library. If this is what you want to do, use the
GNU Lesser General Public License instead of this License. But first,
please read <https://www.gnu.org/licenses/why-not-lgpl.html>.
please read <https://www.gnu.org/licenses/why-not-lgpl.html>.
@@ -8,7 +8,7 @@ apply if there are changes made to it from upstream - thus notifying us
that changes were made.
diff --git a/.gitignore b/.gitignore
index 11038da2e071699d6561a331565db0c8d7850d0e..317acfec5894101294a55abff61819431e6a5e65 100644
index e431e3435737e28394d81b56568a08b3c3148b9b..b23bde3b5e881f146539a307d0a59f2177b6a036 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
@@ -17,7 +17,7 @@ index 11038da2e071699d6561a331565db0c8d7850d0e..317acfec5894101294a55abff6181943
# Eclipse stuff
/.classpath
/.project
@@ -31,3 +33,7 @@
@@ -30,3 +32,7 @@
*.ipr
*.iws
.idea/
@@ -27,10 +27,10 @@ index 11038da2e071699d6561a331565db0c8d7850d0e..317acfec5894101294a55abff6181943
+/.factorypath
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..c2e5d8006420aab5a60e5aaa188223aeb0396483
index 0000000000000000000000000000000000000000..c76bfbcfbe034700bdbd9396643cfca625273a14
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,86 @@
@@ -0,0 +1,66 @@
+plugins {
+ `java-library`
+ `maven-publish`
@@ -41,29 +41,25 @@ index 0000000000000000000000000000000000000000..c2e5d8006420aab5a60e5aaa188223ae
+ withJavadocJar()
+}
+
+val annotationsVersion = "24.0.1"
+val bungeeCordChatVersion = "1.20-R0.1"
+
+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:$bungeeCordChatVersion")
+ api("org.yaml:snakeyaml:2.0")
+ api("org.joml:joml:1.10.5")
+ api("commons-lang:commons-lang:2.6")
+ api("com.google.guava:guava:31.0.1-jre")
+ api("com.google.code.gson:gson:2.8.8")
+ api("net.md-5:bungeecord-chat:1.16-R0.4")
+ api("org.yaml:snakeyaml:1.30")
+
+ 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("org.apache.maven:maven-resolver-provider:3.8.4")
+ compileOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.2")
+ compileOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.2")
+
+ val annotations = "org.jetbrains:annotations-java5:$annotationsVersion"
+ val annotations = "org.jetbrains:annotations-java5:23.0.0"
+ compileOnly(annotations)
+ testCompileOnly(annotations)
+
+ 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.5")
+ testImplementation("org.ow2.asm:asm-tree:9.2")
+}
+
+configure<PublishingExtension> {
@@ -94,35 +90,19 @@ index 0000000000000000000000000000000000000000..c2e5d8006420aab5a60e5aaa188223ae
+}
+
+tasks.withType<Javadoc> {
+ val options = options as StandardJavadocDocletOptions
+ options.overview = "src/main/javadoc/overview.html"
+ options.use()
+ options.isDocFilesSubDirs = true
+ options.links(
+ "https://guava.dev/releases/31.1-jre/api/docs/",
+ "https://javadoc.io/doc/org.yaml/snakeyaml/2.0/",
+ "https://javadoc.io/doc/org.jetbrains/annotations-java5/$annotationsVersion/",
+ "https://javadoc.io/doc/net.md-5/bungeecord-chat/$bungeeCordChatVersion/",
+ (options as StandardJavadocDocletOptions).links(
+ "https://guava.dev/releases/31.0.1-jre/api/docs/",
+ "https://javadoc.io/doc/org.yaml/snakeyaml/1.30/",
+ "https://javadoc.io/doc/org.jetbrains/annotations-java5/23.0.0/",
+ "https://javadoc.io/doc/net.md-5/bungeecord-chat/1.16-R0.4/",
+ )
+ options.tags("apiNote:a:API Note:")
+
+ // workaround for https://github.com/gradle/gradle/issues/4046
+ inputs.dir("src/main/javadoc").withPropertyName("javadoc-sourceset")
+ doLast {
+ copy {
+ from("src/main/javadoc") {
+ include("**/doc-files/**")
+ }
+ into("build/docs/javadoc")
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 068987ca1171857fc9996d645e775448a09f0feb..0000000000000000000000000000000000000000
index 9c130bf7fe7ec4b6b5d4f4c30cdbe6dc75b0f9a0..0000000000000000000000000000000000000000
--- a/pom.xml
+++ /dev/null
@@ -1,270 +0,0 @@
@@ -1,262 +0,0 @@
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -130,7 +110,7 @@ index 068987ca1171857fc9996d645e775448a09f0feb..00000000000000000000000000000000
-
- <groupId>org.spigotmc</groupId>
- <artifactId>spigot-api</artifactId>
- <version>1.20.1-R0.1-SNAPSHOT</version>
- <version>1.18.1-R0.1-SNAPSHOT</version>
- <packaging>jar</packaging>
-
- <name>Spigot-API</name>
@@ -156,64 +136,63 @@ index 068987ca1171857fc9996d645e775448a09f0feb..00000000000000000000000000000000
- </distributionManagement>
-
- <dependencies>
- <dependency>
- <groupId>commons-lang</groupId>
- <artifactId>commons-lang</artifactId>
- <version>2.6</version>
- <scope>compile</scope>
- </dependency>
- <!-- bundled with Minecraft, should be kept in sync -->
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>31.1-jre</version>
- <version>31.0.1-jre</version>
- <scope>compile</scope>
- </dependency>
- <!-- bundled with Minecraft, should be kept in sync -->
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10</version>
- <scope>compile</scope>
- </dependency>
- <!-- bundled with Minecraft, should be kept in sync -->
- <dependency>
- <groupId>org.joml</groupId>
- <artifactId>joml</artifactId>
- <version>1.10.5</version>
- <version>2.8.8</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>net.md-5</groupId>
- <artifactId>bungeecord-chat</artifactId>
- <version>1.20-R0.1</version>
- <version>1.16-R0.4</version>
- <type>jar</type>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.yaml</groupId>
- <artifactId>snakeyaml</artifactId>
- <version>2.0</version>
- <version>1.30</version>
- <scope>compile</scope>
- </dependency>
- <!-- not part of the API proper -->
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-resolver-provider</artifactId>
- <version>3.8.5</version>
- <version>3.8.4</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven.resolver</groupId>
- <artifactId>maven-resolver-connector-basic</artifactId>
- <version>1.7.3</version>
- <version>1.7.2</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven.resolver</groupId>
- <artifactId>maven-resolver-transport-http</artifactId>
- <version>1.7.3</version>
- <version>1.7.2</version>
- <scope>provided</scope>
- </dependency>
- <!-- annotations -->
- <dependency>
- <groupId>org.jetbrains</groupId>
- <artifactId>annotations-java5</artifactId>
- <version>24.0.1</version>
- <version>23.0.0</version>
- <scope>provided</scope>
- </dependency>
- <!-- testing -->
@@ -232,7 +211,7 @@ index 068987ca1171857fc9996d645e775448a09f0feb..00000000000000000000000000000000
- <dependency>
- <groupId>org.ow2.asm</groupId>
- <artifactId>asm-tree</artifactId>
- <version>9.5</version>
- <version>9.2</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
@@ -242,7 +221,7 @@ index 068987ca1171857fc9996d645e775448a09f0feb..00000000000000000000000000000000
- <plugin>
- <groupId>net.md-5</groupId>
- <artifactId>scriptus</artifactId>
- <version>0.5.0</version>
- <version>0.4.1</version>
- <executions>
- <execution>
- <phase>initialize</phase>
@@ -255,25 +234,28 @@ index 068987ca1171857fc9996d645e775448a09f0feb..00000000000000000000000000000000
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.11.0</version>
- <version>3.8.1</version>
- <configuration>
- <!-- we use the Eclipse compiler as it doesn't need a JDK -->
- <compilerId>eclipse</compilerId>
- <!-- default changed with version 3.11.0 -->
- <showWarnings>false</showWarnings>
- </configuration>
- <dependencies>
- <dependency>
- <groupId>org.codehaus.plexus</groupId>
- <artifactId>plexus-compiler-eclipse</artifactId>
- <version>2.13.0</version>
- <version>2.8.8</version>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jdt</groupId>
- <artifactId>ecj</artifactId>
- <version>3.28.0</version>
- </dependency>
- </dependencies>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <version>3.3.0</version>
- <version>3.2.0</version>
- <configuration>
- <archive>
- <manifest>
@@ -288,7 +270,7 @@ index 068987ca1171857fc9996d645e775448a09f0feb..00000000000000000000000000000000
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-shade-plugin</artifactId>
- <version>3.5.0</version>
- <version>3.2.4</version>
- <executions>
- <execution>
- <phase>package</phase>
@@ -308,28 +290,19 @@ index 068987ca1171857fc9996d645e775448a09f0feb..00000000000000000000000000000000
- </filters>
- <!-- when downloading via Maven we can pull depends individually -->
- <shadedArtifactAttached>true</shadedArtifactAttached>
- <!-- In 3.3.0 and later, shadedArtifactAttached causes dependant projects to use dependency reduced pom (bug?) -->
- <createDependencyReducedPom>false</createDependencyReducedPom>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
- <version>3.5.0</version>
- <version>3.3.1</version>
- <configuration>
- <links>
- <link>https://guava.dev/releases/31.1-jre/api/docs/</link>
- <link>https://javadoc.io/doc/org.yaml/snakeyaml/2.0/</link>
- <link>https://javadoc.io/doc/org.jetbrains/annotations-java5/24.0.1/</link>
- <link>https://javadoc.io/doc/net.md-5/bungeecord-chat/1.20-R0.1/</link>
- <link>https://guava.dev/releases/31.0.1-jre/api/docs/</link>
- <link>https://javadoc.io/doc/org.yaml/snakeyaml/1.30/</link>
- <link>https://javadoc.io/doc/org.jetbrains/annotations-java5/23.0.0/</link>
- <link>https://javadoc.io/doc/net.md-5/bungeecord-chat/1.16-R0.4/</link>
- </links>
- <tags>
- <tag>
- <name>apiNote</name>
- <placement>a</placement>
- <head>API Note:</head>
- </tag>
- </tags>
- </configuration>
- </plugin>
- </plugins>
@@ -346,7 +319,7 @@ index 068987ca1171857fc9996d645e775448a09f0feb..00000000000000000000000000000000
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-checkstyle-plugin</artifactId>
- <version>3.3.0</version>
- <version>3.1.2</version>
- <executions>
- <execution>
- <phase>process-classes</phase>
@@ -357,7 +330,6 @@ index 068987ca1171857fc9996d645e775448a09f0feb..00000000000000000000000000000000
- </executions>
- <configuration>
- <configLocation>checkstyle.xml</configLocation>
- <suppressionsLocation>checkstyle-suppressions.xml</suppressionsLocation>
- <includeTestSourceDirectory>true</includeTestSourceDirectory>
- </configuration>
- <dependencies>
@@ -371,7 +343,7 @@ index 068987ca1171857fc9996d645e775448a09f0feb..00000000000000000000000000000000
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>animal-sniffer-maven-plugin</artifactId>
- <version>1.23</version>
- <version>1.20</version>
- <executions>
- <execution>
- <phase>process-classes</phase>
+18 -57
View File
@@ -5,26 +5,26 @@ Subject: [PATCH] Build system changes
diff --git a/build.gradle.kts b/build.gradle.kts
index c2e5d8006420aab5a60e5aaa188223aeb0396483..ffd1c792b0e0fe1ed3da85cf933202c5193734ce 100644
index c76bfbcfbe034700bdbd9396643cfca625273a14..1f680baffba17265e8ce38b3516624bffd034075 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -18,15 +18,27 @@ dependencies {
api("net.md-5:bungeecord-chat:$bungeeCordChatVersion")
api("org.yaml:snakeyaml:2.0")
api("org.joml:joml:1.10.5")
@@ -15,15 +15,27 @@ dependencies {
api("com.google.code.gson:gson:2.8.8")
api("net.md-5:bungeecord-chat:1.16-R0.4")
api("org.yaml:snakeyaml:1.30")
+ // Paper start
+ api("com.googlecode.json-simple:json-simple:1.1.1") {
+ isTransitive = false // includes junit
+ }
+ // 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("org.apache.maven:maven-resolver-provider:3.8.4")
compileOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.2")
compileOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.2")
+ compileOnly("com.google.code.findbugs:jsr305:1.3.9") // Paper
- val annotations = "org.jetbrains:annotations-java5:$annotationsVersion"
+ val annotations = "org.jetbrains:annotations:$annotationsVersion" // Paper - we don't want Java 5 annotations...
- val annotations = "org.jetbrains:annotations-java5:23.0.0"
+ val annotations = "org.jetbrains:annotations:23.0.0" // Paper - we don't want Java 5 annotations...
compileOnly(annotations)
testCompileOnly(annotations)
@@ -34,54 +34,15 @@ index c2e5d8006420aab5a60e5aaa188223aeb0396483..ffd1c792b0e0fe1ed3da85cf933202c5
+ testCompileOnly(checkerQual)
+ // Paper end
+
testImplementation("org.apache.commons:commons-lang3:3.12.0")
testImplementation("junit:junit:4.13.2")
testImplementation("org.hamcrest:hamcrest-library:1.3")
@@ -68,7 +80,7 @@ tasks.withType<Javadoc> {
options.links(
"https://guava.dev/releases/31.1-jre/api/docs/",
"https://javadoc.io/doc/org.yaml/snakeyaml/2.0/",
- "https://javadoc.io/doc/org.jetbrains/annotations-java5/$annotationsVersion/",
+ "https://javadoc.io/doc/org.jetbrains/annotations/$annotationsVersion/", // Paper - we don't want Java 5 annotations
"https://javadoc.io/doc/net.md-5/bungeecord-chat/$bungeeCordChatVersion/",
testImplementation("org.ow2.asm:asm-tree:9.2")
@@ -60,7 +72,7 @@ tasks.withType<Javadoc> {
(options as StandardJavadocDocletOptions).links(
"https://guava.dev/releases/31.0.1-jre/api/docs/",
"https://javadoc.io/doc/org.yaml/snakeyaml/1.30/",
- "https://javadoc.io/doc/org.jetbrains/annotations-java5/23.0.0/",
+ "https://javadoc.io/doc/org.jetbrains/annotations/23.0.0/", // Paper - we don't want Java 5 annotations
"https://javadoc.io/doc/net.md-5/bungeecord-chat/1.16-R0.4/",
)
options.tags("apiNote:a:API Note:")
@@ -84,3 +96,14 @@ tasks.withType<Javadoc> {
}
}
}
+
+// Paper start
+val scanJar = tasks.register("scanJarForBadCalls", io.papermc.paperweight.tasks.ScanJarForBadCalls::class) {
+ badAnnotations.add("Lio/papermc/paper/annotation/DoNotUse;")
+ jarToScan.set(tasks.jar.flatMap { it.archiveFile })
+ classpath.from(configurations.compileClasspath)
+}
+tasks.check {
+ dependsOn(scanJar)
+}
+// Paper end
diff --git a/src/main/java/io/papermc/paper/annotation/DoNotUse.java b/src/main/java/io/papermc/paper/annotation/DoNotUse.java
new file mode 100644
index 0000000000000000000000000000000000000000..4766e49d819e75e5c2127c698b44078bf2fd6219
--- /dev/null
+++ b/src/main/java/io/papermc/paper/annotation/DoNotUse.java
@@ -0,0 +1,18 @@
+package io.papermc.paper.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * Annotation used to mark methods or constructors which should not be called.
+ *
+ * <p>Separate from {@link Deprecated} to differentiate from the large amount of deprecations.</p>
+ */
+@ApiStatus.Internal
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+public @interface DoNotUse {
+}
@@ -0,0 +1,114 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Sun, 17 Mar 2019 23:04:30 +0000
Subject: [PATCH] Annotation Test changes
- Allow use of TYPE_USE annotations
- Ignore package-private methods for nullability annotations
- Add excludes for classes which don't pass
diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java
index 0c7377247ad9251c9e498039511e7220370aba2d..c6a8a37a933cfc5a5885602a8a70fdda8fb6aa10 100644
--- a/src/test/java/org/bukkit/AnnotationTest.java
+++ b/src/test/java/org/bukkit/AnnotationTest.java
@@ -40,7 +40,17 @@ public class AnnotationTest {
"org/bukkit/util/io/Wrapper",
"org/bukkit/plugin/java/PluginClassLoader",
// Generic functional interface
- "org/bukkit/util/Consumer"
+ "org/bukkit/util/Consumer",
+ // Paper start
+ // Timings history is broken in terms of nullability due to guavas Function defining that the param is NonNull
+ "co/aikar/timings/TimingHistory$2",
+ "co/aikar/timings/TimingHistory$2$1",
+ "co/aikar/timings/TimingHistory$2$1$1",
+ "co/aikar/timings/TimingHistory$2$1$2",
+ "co/aikar/timings/TimingHistory$3",
+ "co/aikar/timings/TimingHistory$4",
+ "co/aikar/timings/TimingHistoryEntry$1"
+ // Paper end
};
@Test
@@ -67,14 +77,40 @@ public class AnnotationTest {
}
if (mustBeAnnotated(Type.getReturnType(method.desc)) && !isWellAnnotated(method.invisibleAnnotations)) {
+ // Paper start - Allow use of TYPE_USE annotations
+ boolean warn = true;
+ if (isWellAnnotated(method.visibleTypeAnnotations)) {
+ warn = false;
+ } else if (method.invisibleTypeAnnotations != null) {
+ dance: for (final org.objectweb.asm.tree.TypeAnnotationNode invisibleTypeAnnotation : method.invisibleTypeAnnotations) {
+ final org.objectweb.asm.TypeReference ref = new org.objectweb.asm.TypeReference(invisibleTypeAnnotation.typeRef);
+ if (ref.getSort() == org.objectweb.asm.TypeReference.METHOD_RETURN && java.util.Arrays.binarySearch(ACCEPTED_ANNOTATIONS, invisibleTypeAnnotation.desc) >= 0) {
+ warn = false;
+ break dance; // cha cha real smooth
+ }
+ }
+ }
+ if (warn)
+ // Paper end
warn(errors, clazz, method, "return value");
}
Type[] paramTypes = Type.getArgumentTypes(method.desc);
List<ParameterNode> parameters = method.parameters;
+ dancing: // Paper
for (int i = 0; i < paramTypes.length; i++) {
if (mustBeAnnotated(paramTypes[i]) && !isWellAnnotated(method.invisibleParameterAnnotations == null ? null : method.invisibleParameterAnnotations[i])) {
+ // Paper start
+ if (method.invisibleTypeAnnotations != null) {
+ for (final org.objectweb.asm.tree.TypeAnnotationNode invisibleTypeAnnotation : method.invisibleTypeAnnotations) {
+ final org.objectweb.asm.TypeReference ref = new org.objectweb.asm.TypeReference(invisibleTypeAnnotation.typeRef);
+ if (ref.getSort() == org.objectweb.asm.TypeReference.METHOD_FORMAL_PARAMETER && ref.getTypeParameterIndex() == i && java.util.Arrays.binarySearch(ACCEPTED_ANNOTATIONS, invisibleTypeAnnotation.desc) >= 0) {
+ continue dancing;
+ }
+ }
+ }
+ // Paper end - Allow use of TYPE_USE annotations
ParameterNode paramNode = parameters == null ? null : parameters.get(i);
String paramName = paramNode == null ? null : paramNode.name;
@@ -91,13 +127,18 @@ public class AnnotationTest {
Collections.sort(errors);
- System.out.println(errors.size() + " missing annotation(s):");
+ StringBuilder builder = new StringBuilder()
+ .append("There ")
+ .append(errors.size() != 1 ? "are " : "is ")
+ .append(errors.size())
+ .append(" missing annotation")
+ .append(errors.size() != 1 ? "s:\n" : ":\n");
+
for (String message : errors) {
- System.out.print("\t");
- System.out.println(message);
+ builder.append("\t").append(message).append("\n");
}
- Assert.fail("There " + errors.size() + " are missing annotation(s)");
+ Assert.fail(builder.toString());
}
private static void collectClasses(@NotNull File from, @NotNull Map<String, ClassNode> to) throws IOException {
@@ -152,7 +193,7 @@ public class AnnotationTest {
private static boolean isMethodIncluded(@NotNull ClassNode clazz, @NotNull MethodNode method, @NotNull Map<String, ClassNode> allClasses) {
// Exclude private, synthetic and deprecated methods
- if ((method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_DEPRECATED)) != 0) {
+ if ((method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_DEPRECATED)) != 0 || (method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) == 0) { // Paper - ignore package-private
return false;
}
@@ -174,7 +215,7 @@ public class AnnotationTest {
return true;
}
- private static boolean isWellAnnotated(@Nullable List<AnnotationNode> annotations) {
+ private static boolean isWellAnnotated(@Nullable List<? extends AnnotationNode> annotations) { // Paper
if (annotations == null) {
return false;
}
-542
View File
@@ -1,542 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Sun, 17 Mar 2019 23:04:30 +0000
Subject: [PATCH] Test changes
- convert to mockito for mocking of types
- Allow use of TYPE_USE annotations
- Ignore package-private methods for nullability annotations
- Add excludes for classes which don't pass
- Disable stupid BukkitMirrorTest
Co-authored-by: Riley Park <rileysebastianpark@gmail.com>
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/build.gradle.kts b/build.gradle.kts
index ffd1c792b0e0fe1ed3da85cf933202c5193734ce..b38a9f4d628fa639e5d2b7dcd5a63f0bf6b8330b 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -38,6 +38,7 @@ dependencies {
compileOnlyApi(checkerQual)
testCompileOnly(checkerQual)
// Paper end
+ testImplementation("org.mockito:mockito-core:4.9.0") // Paper - add mockito
testImplementation("org.apache.commons:commons-lang3:3.12.0")
testImplementation("junit:junit:4.13.2")
diff --git a/src/test/java/io/papermc/paper/testing/EmptyRegistry.java b/src/test/java/io/papermc/paper/testing/EmptyRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba9ddce87a9f385e729a5c2cf7c5eec120e388a7
--- /dev/null
+++ b/src/test/java/io/papermc/paper/testing/EmptyRegistry.java
@@ -0,0 +1,23 @@
+package io.papermc.paper.testing;
+
+import java.util.Collections;
+import java.util.Iterator;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public record EmptyRegistry() implements Registry<Keyed> {
+
+ @NotNull
+ @Override
+ public Iterator<Keyed> iterator() {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public @Nullable Keyed get(@NotNull final NamespacedKey key) {
+ return null;
+ }
+}
diff --git a/src/test/java/io/papermc/paper/testing/EmptyTag.java b/src/test/java/io/papermc/paper/testing/EmptyTag.java
new file mode 100644
index 0000000000000000000000000000000000000000..77154095cfb8b259bdb318e8ff40cb6f559ebc18
--- /dev/null
+++ b/src/test/java/io/papermc/paper/testing/EmptyTag.java
@@ -0,0 +1,31 @@
+package io.papermc.paper.testing;
+
+import java.util.Collections;
+import java.util.Set;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Tag;
+import org.jetbrains.annotations.NotNull;
+
+public record EmptyTag(NamespacedKey key) implements Tag<Keyed> {
+
+ @SuppressWarnings("deprecation")
+ public EmptyTag() {
+ this(NamespacedKey.randomKey());
+ }
+
+ @Override
+ public @NotNull NamespacedKey getKey() {
+ return this.key;
+ }
+
+ @Override
+ public boolean isTagged(@NotNull final Keyed item) {
+ return false;
+ }
+
+ @Override
+ public @NotNull Set<Keyed> getValues() {
+ return Collections.emptySet();
+ }
+}
diff --git a/src/test/java/io/papermc/paper/testing/TestServer.java b/src/test/java/io/papermc/paper/testing/TestServer.java
new file mode 100644
index 0000000000000000000000000000000000000000..756acf231b1b076b08046d86992ba7ce7f62a94f
--- /dev/null
+++ b/src/test/java/io/papermc/paper/testing/TestServer.java
@@ -0,0 +1,48 @@
+package io.papermc.paper.testing;
+
+import java.util.logging.Logger;
+import org.bukkit.Bukkit;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Server;
+import org.bukkit.command.SimpleCommandMap;
+import org.bukkit.plugin.PluginManager;
+import org.bukkit.plugin.SimplePluginManager;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public final class TestServer {
+
+ @SuppressWarnings("removal")
+ public static void setup() {
+ //noinspection ConstantValue
+ if (Bukkit.getServer() != null) {
+ return;
+ }
+
+ final Server dummyServer = mock(Server.class);
+
+ final Logger logger = Logger.getLogger(TestServer.class.getCanonicalName());
+ when(dummyServer.getLogger()).thenReturn(logger);
+ when(dummyServer.getName()).thenReturn(TestServer.class.getSimpleName());
+ when(dummyServer.getVersion()).thenReturn("Version_" + TestServer.class.getPackage().getImplementationVersion());
+ when(dummyServer.getBukkitVersion()).thenReturn("BukkitVersion_" + TestServer.class.getPackage().getImplementationVersion());
+
+
+ final Thread currentThread = Thread.currentThread();
+ when(dummyServer.isPrimaryThread()).thenAnswer(ignored -> Thread.currentThread().equals(currentThread));
+
+ when(dummyServer.getTag(anyString(), any(NamespacedKey.class), any())).thenAnswer(ignored -> new EmptyTag());
+
+ final PluginManager pluginManager = new SimplePluginManager(dummyServer, new SimpleCommandMap(dummyServer));
+ when(dummyServer.getPluginManager()).thenReturn(pluginManager);
+
+ when(dummyServer.getRegistry(any())).thenAnswer(ignored -> new EmptyRegistry());
+ when(dummyServer.getScoreboardCriteria(anyString())).thenReturn(null);
+
+ Bukkit.setServer(dummyServer);
+ }
+
+}
diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java
index 4ac3dd977e75cd8464163351d306e037ee32cb48..c26ea217927ba77611e6ae93f8df50a83bceb3dd 100644
--- a/src/test/java/org/bukkit/AnnotationTest.java
+++ b/src/test/java/org/bukkit/AnnotationTest.java
@@ -29,7 +29,13 @@ public class AnnotationTest {
"Lorg/jetbrains/annotations/Nullable;",
"Lorg/jetbrains/annotations/NotNull;",
"Lorg/jetbrains/annotations/Contract;",
- "Lorg/bukkit/UndefinedNullability;"
+ "Lorg/bukkit/UndefinedNullability;",
+ // Paper start
+ "Lorg/checkerframework/checker/nullness/qual/MonotonicNonNull;",
+ "Lorg/checkerframework/checker/nullness/qual/NonNull;",
+ "Lorg/checkerframework/checker/nullness/qual/Nullable;",
+ "Lorg/checkerframework/checker/nullness/qual/PolyNull;",
+ // Paper end
};
private static final String[] EXCLUDED_CLASSES = {
@@ -40,7 +46,17 @@ public class AnnotationTest {
"org/bukkit/util/io/Wrapper",
"org/bukkit/plugin/java/PluginClassLoader",
// Generic functional interface
- "org/bukkit/util/Consumer"
+ "org/bukkit/util/Consumer",
+ // Paper start
+ // Timings history is broken in terms of nullability due to guavas Function defining that the param is NonNull
+ "co/aikar/timings/TimingHistory$2",
+ "co/aikar/timings/TimingHistory$2$1",
+ "co/aikar/timings/TimingHistory$2$1$1",
+ "co/aikar/timings/TimingHistory$2$1$2",
+ "co/aikar/timings/TimingHistory$3",
+ "co/aikar/timings/TimingHistory$4",
+ "co/aikar/timings/TimingHistoryEntry$1"
+ // Paper end
};
@Test
@@ -67,14 +83,40 @@ public class AnnotationTest {
}
if (mustBeAnnotated(Type.getReturnType(method.desc)) && !isWellAnnotated(method.invisibleAnnotations)) {
+ // Paper start - Allow use of TYPE_USE annotations
+ boolean warn = true;
+ if (isWellAnnotated(method.visibleTypeAnnotations)) {
+ warn = false;
+ } else if (method.invisibleTypeAnnotations != null) {
+ dance: for (final org.objectweb.asm.tree.TypeAnnotationNode invisibleTypeAnnotation : method.invisibleTypeAnnotations) {
+ final org.objectweb.asm.TypeReference ref = new org.objectweb.asm.TypeReference(invisibleTypeAnnotation.typeRef);
+ if (ref.getSort() == org.objectweb.asm.TypeReference.METHOD_RETURN && java.util.Arrays.asList(ACCEPTED_ANNOTATIONS).contains(invisibleTypeAnnotation.desc)) {
+ warn = false;
+ break dance; // cha cha real smooth
+ }
+ }
+ }
+ if (warn)
+ // Paper end
warn(errors, clazz, method, "return value");
}
Type[] paramTypes = Type.getArgumentTypes(method.desc);
List<ParameterNode> parameters = method.parameters;
+ dancing: // Paper
for (int i = 0; i < paramTypes.length; i++) {
if (mustBeAnnotated(paramTypes[i]) ^ isWellAnnotated(method.invisibleParameterAnnotations == null ? null : method.invisibleParameterAnnotations[i])) {
+ // Paper start
+ if (method.invisibleTypeAnnotations != null) {
+ for (final org.objectweb.asm.tree.TypeAnnotationNode invisibleTypeAnnotation : method.invisibleTypeAnnotations) {
+ final org.objectweb.asm.TypeReference ref = new org.objectweb.asm.TypeReference(invisibleTypeAnnotation.typeRef);
+ if (ref.getSort() == org.objectweb.asm.TypeReference.METHOD_FORMAL_PARAMETER && ref.getTypeParameterIndex() == i && java.util.Arrays.asList(ACCEPTED_ANNOTATIONS).contains(invisibleTypeAnnotation.desc)) {
+ continue dancing;
+ }
+ }
+ }
+ // Paper end - Allow use of TYPE_USE annotations
ParameterNode paramNode = parameters == null ? null : parameters.get(i);
String paramName = paramNode == null ? null : paramNode.name;
@@ -91,13 +133,18 @@ public class AnnotationTest {
Collections.sort(errors);
- System.out.println(errors.size() + " missing annotation(s):");
+ StringBuilder builder = new StringBuilder()
+ .append("There ")
+ .append(errors.size() != 1 ? "are " : "is ")
+ .append(errors.size())
+ .append(" missing annotation")
+ .append(errors.size() != 1 ? "s:\n" : ":\n");
+
for (String message : errors) {
- System.out.print("\t");
- System.out.println(message);
+ builder.append("\t").append(message).append("\n");
}
- Assert.fail("There " + errors.size() + " are missing annotation(s)");
+ Assert.fail(builder.toString());
}
private static void collectClasses(@NotNull File from, @NotNull Map<String, ClassNode> to) throws IOException {
@@ -140,6 +187,11 @@ public class AnnotationTest {
// Exceptions are excluded
return false;
}
+ // Paper start
+ if (isInternal(clazz.invisibleAnnotations)) {
+ return false;
+ }
+ // Paper end
for (String excludedClass : EXCLUDED_CLASSES) {
if (excludedClass.equals(clazz.name)) {
@@ -152,7 +204,7 @@ public class AnnotationTest {
private static boolean isMethodIncluded(@NotNull ClassNode clazz, @NotNull MethodNode method, @NotNull Map<String, ClassNode> allClasses) {
// Exclude private, synthetic and deprecated methods
- if ((method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_DEPRECATED)) != 0) {
+ if ((method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_DEPRECATED)) != 0 || (method.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) == 0) { // Paper - ignore package-private
return false;
}
@@ -170,11 +222,30 @@ public class AnnotationTest {
if ("<init>".equals(method.name) && isAnonymous(clazz)) {
return false;
}
+ // Paper start
+ if (isInternal(method.invisibleAnnotations)) {
+ return false;
+ }
+ // Paper end
return true;
}
+ // Paper start
+ private static boolean isInternal(List<? extends AnnotationNode> annotations) {
+ if (annotations == null) {
+ return false;
+ }
+ for (AnnotationNode node : annotations) {
+ if (node.desc.equals("Lorg/jetbrains/annotations/ApiStatus$Internal;")) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ // Paper end
- private static boolean isWellAnnotated(@Nullable List<AnnotationNode> annotations) {
+ private static boolean isWellAnnotated(@Nullable List<? extends AnnotationNode> annotations) { // Paper
if (annotations == null) {
return false;
}
diff --git a/src/test/java/org/bukkit/BukkitMirrorTest.java b/src/test/java/org/bukkit/BukkitMirrorTest.java
index 2dfada66067d79b84cd490eadbe0178e8cd8c260..06d66512c91c680130132b79e34fbf32245ff9ee 100644
--- a/src/test/java/org/bukkit/BukkitMirrorTest.java
+++ b/src/test/java/org/bukkit/BukkitMirrorTest.java
@@ -20,6 +20,7 @@ public class BukkitMirrorTest {
@Parameters(name = "{index}: {1}")
public static List<Object[]> data() {
+ if (true) return List.of(); // Paper
return Lists.transform(Arrays.asList(Server.class.getDeclaredMethods()), new Function<Method, Object[]>() {
@Override
public Object[] apply(Method input) {
diff --git a/src/test/java/org/bukkit/TestServer.java b/src/test/java/org/bukkit/TestServer.java
deleted file mode 100644
index 701a17c10f31cd345238a3c568264178ce372faa..0000000000000000000000000000000000000000
--- a/src/test/java/org/bukkit/TestServer.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package org.bukkit;
-
-import com.google.common.collect.ImmutableMap;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.logging.Logger;
-import org.bukkit.command.SimpleCommandMap;
-import org.bukkit.plugin.PluginManager;
-import org.bukkit.plugin.SimplePluginManager;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public final class TestServer implements InvocationHandler {
- private static interface MethodHandler {
- Object handle(TestServer server, Object[] args);
- }
-
- private static final Map<Method, MethodHandler> methods;
-
- static {
- try {
- ImmutableMap.Builder<Method, MethodHandler> methodMap = ImmutableMap.builder();
- methodMap.put(
- Server.class.getMethod("isPrimaryThread"),
- new MethodHandler() {
- @Override
- public Object handle(TestServer server, Object[] args) {
- return Thread.currentThread().equals(server.creatingThread);
- }
- }
- );
- methodMap.put(
- Server.class.getMethod("getPluginManager"),
- new MethodHandler() {
- @Override
- public Object handle(TestServer server, Object[] args) {
- return server.pluginManager;
- }
- }
- );
- methodMap.put(
- Server.class.getMethod("getLogger"),
- new MethodHandler() {
- final Logger logger = Logger.getLogger(TestServer.class.getCanonicalName());
- @Override
- public Object handle(TestServer server, Object[] args) {
- return logger;
- }
- }
- );
- methodMap.put(
- Server.class.getMethod("getName"),
- new MethodHandler() {
- @Override
- public Object handle(TestServer server, Object[] args) {
- return TestServer.class.getSimpleName();
- }
- }
- );
- methodMap.put(
- Server.class.getMethod("getVersion"),
- new MethodHandler() {
- @Override
- public Object handle(TestServer server, Object[] args) {
- return "Version_" + TestServer.class.getPackage().getImplementationVersion();
- }
- }
- );
- methodMap.put(
- Server.class.getMethod("getBukkitVersion"),
- new MethodHandler() {
- @Override
- public Object handle(TestServer server, Object[] args) {
- return "BukkitVersion_" + TestServer.class.getPackage().getImplementationVersion();
- }
- }
- );
- methodMap.put(
- Server.class.getMethod("getRegistry", Class.class),
- new MethodHandler() {
- @Override
- public Object handle(TestServer server, Object[] args) {
- return new Registry() {
- @NotNull
- @Override
- public Iterator iterator() {
- return null;
- }
-
- @Nullable
- @Override
- public Keyed get(@NotNull NamespacedKey key) {
- return null;
- }
- };
- }
- }
- );
- methodMap.put(
- Server.class.getMethod("getScoreboardCriteria", String.class),
- new MethodHandler() {
- @Override
- public Object handle(TestServer server, Object[] args) {
- // Does not need to return anything. Exists solely to test CriteriaTest which has static init fields
- return null;
- }
- }
- );
- methods = methodMap.build();
-
- TestServer server = new TestServer();
- Server instance = Proxy.getProxyClass(Server.class.getClassLoader(), Server.class).asSubclass(Server.class).getConstructor(InvocationHandler.class).newInstance(server);
- Bukkit.setServer(instance);
- server.pluginManager = new SimplePluginManager(instance, new SimpleCommandMap(instance));
- } catch (Throwable t) {
- throw new Error(t);
- }
- }
-
- private Thread creatingThread = Thread.currentThread();
- private PluginManager pluginManager;
- private TestServer() {};
-
- public static Server getInstance() {
- return Bukkit.getServer();
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) {
- MethodHandler handler = methods.get(method);
- if (handler != null) {
- return handler.handle(this, args);
- }
- throw new UnsupportedOperationException(String.valueOf(method));
- }
-}
diff --git a/src/test/java/org/bukkit/TestWorld.java b/src/test/java/org/bukkit/TestWorld.java
index ab34f1199921d415fa2ca6e281a8125c9e6d7173..f64d024f5bbf9482aaddb56597b23b04c66f21bf 100644
--- a/src/test/java/org/bukkit/TestWorld.java
+++ b/src/test/java/org/bukkit/TestWorld.java
@@ -18,7 +18,7 @@ public final class TestWorld implements InvocationHandler {
static {
try {
- TestServer.getInstance();
+ io.papermc.paper.testing.TestServer.setup(); // Paper
ImmutableMap.Builder<Method, MethodHandler> methodMap = ImmutableMap.builder();
methodMap.put(
diff --git a/src/test/java/org/bukkit/event/SyntheticEventTest.java b/src/test/java/org/bukkit/event/SyntheticEventTest.java
index d402cb59f508205ebe9ee450594826b04cecb90b..09886568ae6167141b463b6262565fa212af3385 100644
--- a/src/test/java/org/bukkit/event/SyntheticEventTest.java
+++ b/src/test/java/org/bukkit/event/SyntheticEventTest.java
@@ -1,6 +1,5 @@
package org.bukkit.event;
-import org.bukkit.TestServer;
import org.bukkit.plugin.PluginLoader;
import org.bukkit.plugin.SimplePluginManager;
import org.bukkit.plugin.TestPlugin;
@@ -12,14 +11,15 @@ public class SyntheticEventTest {
@SuppressWarnings("deprecation")
@Test
public void test() {
- final JavaPluginLoader loader = new JavaPluginLoader(TestServer.getInstance());
+ io.papermc.paper.testing.TestServer.setup(); // Paper
+ final JavaPluginLoader loader = new JavaPluginLoader(org.bukkit.Bukkit.getServer()); // Paper
TestPlugin plugin = new TestPlugin(getClass().getName()) {
@Override
public PluginLoader getPluginLoader() {
return loader;
}
};
- SimplePluginManager pluginManager = new SimplePluginManager(TestServer.getInstance(), null);
+ SimplePluginManager pluginManager = new SimplePluginManager(org.bukkit.Bukkit.getServer(), null); // Paper
TestEvent event = new TestEvent(false);
Impl impl = new Impl();
diff --git a/src/test/java/org/bukkit/plugin/PluginManagerTest.java b/src/test/java/org/bukkit/plugin/PluginManagerTest.java
index f188cd4f3b07027c30d41f1162db77a506b7b6bb..c46ed2acb82db814d660459b705dd49e6d44240f 100644
--- a/src/test/java/org/bukkit/plugin/PluginManagerTest.java
+++ b/src/test/java/org/bukkit/plugin/PluginManagerTest.java
@@ -2,7 +2,6 @@ package org.bukkit.plugin;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
-import org.bukkit.TestServer;
import org.bukkit.event.Event;
import org.bukkit.event.TestEvent;
import org.bukkit.permissions.Permission;
@@ -14,7 +13,7 @@ public class PluginManagerTest {
volatile Object value = null;
}
- private static final PluginManager pm = TestServer.getInstance().getPluginManager();
+ private static final PluginManager pm = org.bukkit.Bukkit.getServer().getPluginManager(); // Paper
private final MutableObject store = new MutableObject();
diff --git a/src/test/java/org/bukkit/scoreboard/CriteriaTest.java b/src/test/java/org/bukkit/scoreboard/CriteriaTest.java
index eb94b6f4d58cd9f66b07791c57af7e359992e28c..a93f28e2f987a36e2c7e4f7d31506b750bdb222b 100644
--- a/src/test/java/org/bukkit/scoreboard/CriteriaTest.java
+++ b/src/test/java/org/bukkit/scoreboard/CriteriaTest.java
@@ -2,7 +2,6 @@ package org.bukkit.scoreboard;
import org.bukkit.Material;
import org.bukkit.Statistic;
-import org.bukkit.TestServer;
import org.bukkit.entity.EntityType;
import org.junit.Assert;
import org.junit.Test;
@@ -11,7 +10,7 @@ public class CriteriaTest {
@Test
public void testStatistic() {
- TestServer.getInstance();
+ io.papermc.paper.testing.TestServer.setup(); // Paper
Assert.assertThrows(IllegalArgumentException.class, () -> Criteria.statistic(Statistic.AVIATE_ONE_CM, Material.STONE)); // Generic statistic with block
Assert.assertThrows(IllegalArgumentException.class, () -> Criteria.statistic(Statistic.AVIATE_ONE_CM, EntityType.CREEPER)); // Generic statistic with entity type
@@ -6,14 +6,14 @@ Subject: [PATCH] Add FastUtil to Bukkit
Doesn't expose to plugins, just allows Paper-API to use it for optimization
diff --git a/build.gradle.kts b/build.gradle.kts
index b38a9f4d628fa639e5d2b7dcd5a63f0bf6b8330b..19720f73cb8454d6ddd8d75e7d08556d61436505 100644
index 364b6215d2facb0273a67ff605ccb8e707e080b8..3777d12874ede329410a6bf1c0533deb44924d73 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -22,6 +22,7 @@ dependencies {
@@ -19,6 +19,7 @@ dependencies {
api("com.googlecode.json-simple:json-simple:1.1.1") {
isTransitive = false // includes junit
}
+ api("it.unimi.dsi:fastutil:8.5.6")
// Paper end
compileOnly("org.apache.maven:maven-resolver-provider:3.8.5")
compileOnly("org.apache.maven:maven-resolver-provider:3.8.4")
File diff suppressed because it is too large. Load diff
File renamed without changes.
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,181 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Tue, 18 May 2021 14:42:26 -0700
Subject: [PATCH] Add command line option to load extra plugin jars not in the
plugins folder
ex: java -jar paperclip.jar nogui -add-plugin=/path/to/plugin.jar -add-plugin=/path/to/another/plugin_jar.jar
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index a238302f2a12194aa30a88867070705dc749b36b..cc42bfa74b41ef6d6374efa7b882f71677fb0824 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -73,6 +73,20 @@ public final class Bukkit {
return server;
}
+ /**
+ * Returns the de facto plugins directory, generally used for storing plugin jars to be loaded,
+ * as well as their {@link org.bukkit.plugin.Plugin#getDataFolder() data folders}.
+ *
+ * <p>Plugins should use {@link org.bukkit.plugin.Plugin#getDataFolder()} rather than traversing this
+ * directory manually when determining the location in which to store their data and configuration files.</p>
+ *
+ * @return plugins directory
+ */
+ @NotNull
+ public static File getPluginsFolder() {
+ return server.getPluginsFolder();
+ }
+
/**
* Attempts to set the {@link Server} singleton.
* <p>
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 3e91c4000c468fd8bdcb938e942a7bbf4988cab2..1fb1d4f32af8150711ca766fcd7d0a0c177df7c4 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -58,6 +58,18 @@ import org.jetbrains.annotations.Nullable;
*/
public interface Server extends PluginMessageRecipient {
+ /**
+ * Returns the de facto plugins directory, generally used for storing plugin jars to be loaded,
+ * as well as their {@link org.bukkit.plugin.Plugin#getDataFolder() data folders}.
+ *
+ * <p>Plugins should use {@link org.bukkit.plugin.Plugin#getDataFolder()} rather than traversing this
+ * directory manually when determining the location in which to store their data and configuration files.</p>
+ *
+ * @return plugins directory
+ */
+ @NotNull
+ File getPluginsFolder();
+
/**
* Used for all administrative messages, such as an operator using a
* command.
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index 7548e40af8043c1b5716f2d7d0122833466854c4..83d3ee09cc6f91b095f6b8519a431ef70f2b67ec 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -111,6 +111,12 @@ public final class SimplePluginManager implements PluginManager {
@Override
@NotNull
public Plugin[] loadPlugins(@NotNull File directory) {
+ // Paper start - extra jars
+ return this.loadPlugins(directory, java.util.Collections.emptyList());
+ }
+ @NotNull
+ public Plugin[] loadPlugins(final @NotNull File directory, final @NotNull List<File> extraPluginJars) {
+ // Paper end
Validate.notNull(directory, "Directory cannot be null");
Validate.isTrue(directory.isDirectory(), "Directory must be a directory");
@@ -128,7 +134,11 @@ public final class SimplePluginManager implements PluginManager {
Map<String, Collection<String>> softDependencies = new HashMap<String, Collection<String>>();
// This is where it figures out all possible plugins
- for (File file : directory.listFiles()) {
+ // Paper start - extra jars
+ final List<File> pluginJars = new ArrayList<>(java.util.Arrays.asList(directory.listFiles()));
+ pluginJars.addAll(extraPluginJars);
+ for (File file : pluginJars) {
+ // Paper end
PluginLoader loader = null;
for (Pattern filter : filters) {
Matcher match = filter.matcher(file.getName());
@@ -144,14 +154,14 @@ public final class SimplePluginManager implements PluginManager {
description = loader.getPluginDescription(file);
String name = description.getName();
if (name.equalsIgnoreCase("bukkit") || name.equalsIgnoreCase("minecraft") || name.equalsIgnoreCase("mojang")) {
- server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': Restricted Name");
+ server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + file.getParentFile().getPath() + "': Restricted Name"); // Paper
continue;
} else if (description.rawName.indexOf(' ') != -1) {
- server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': uses the space-character (0x20) in its name");
+ server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + file.getParentFile().getPath() + "': uses the space-character (0x20) in its name"); // Paper
continue;
}
} catch (InvalidDescriptionException ex) {
- server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
+ server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + file.getParentFile().getPath() + "'", ex); // Paper
continue;
}
@@ -162,7 +172,7 @@ public final class SimplePluginManager implements PluginManager {
description.getName(),
file.getPath(),
replacedFile.getPath(),
- directory.getPath()
+ file.getParentFile().getPath() // Paper
));
}
@@ -183,7 +193,7 @@ public final class SimplePluginManager implements PluginManager {
file.getPath(),
provided,
pluginFile.getPath(),
- directory.getPath()
+ file.getParentFile().getPath() // Paper
));
} else {
String replacedPlugin = pluginsProvided.put(provided, description.getName());
@@ -265,7 +275,7 @@ public final class SimplePluginManager implements PluginManager {
server.getLogger().log(
Level.SEVERE,
- "Could not load '" + entry.getValue().getPath() + "' in folder '" + directory.getPath() + "'",
+ "Could not load '" + entry.getValue().getPath() + "' in folder '" + entry.getValue().getParentFile().getPath() + "'", // Paper
new UnknownDependencyException("Unknown dependency " + dependency + ". Please download and install " + dependency + " to run this plugin."));
break;
}
@@ -304,11 +314,11 @@ public final class SimplePluginManager implements PluginManager {
loadedPlugins.add(loadedPlugin.getName());
loadedPlugins.addAll(loadedPlugin.getDescription().getProvides());
} else {
- server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'");
+ server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + file.getParentFile().getPath() + "'"); // Paper
}
continue;
} catch (InvalidPluginException ex) {
- server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
+ server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + file.getParentFile().getPath() + "'", ex); // Paper
}
}
}
@@ -335,11 +345,11 @@ public final class SimplePluginManager implements PluginManager {
loadedPlugins.add(loadedPlugin.getName());
loadedPlugins.addAll(loadedPlugin.getDescription().getProvides());
} else {
- server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'");
+ server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + file.getParentFile().getPath() + "'"); // Paper
}
break;
} catch (InvalidPluginException ex) {
- server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
+ server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + file.getParentFile().getPath() + "'", ex); // Paper
}
}
}
@@ -352,7 +362,7 @@ public final class SimplePluginManager implements PluginManager {
while (failedPluginIterator.hasNext()) {
File file = failedPluginIterator.next();
failedPluginIterator.remove();
- server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': circular dependency detected");
+ server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + file.getParentFile().getPath() + "': circular dependency detected"); // Paper
}
}
}
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
index cf2f517765d8f2a23cc4a17d9ee2dcd81f841b1b..9700ed558e65260264279b92dc661f39a0d0209c 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
@@ -91,7 +91,7 @@ public final class JavaPluginLoader implements PluginLoader {
throw new InvalidPluginException(ex);
}
- final File parentFile = file.getParentFile();
+ final File parentFile = this.server.getPluginsFolder(); // Paper
final File dataFolder = new File(parentFile, description.getName());
@SuppressWarnings("deprecation")
final File oldDataFolder = new File(parentFile, description.getRawName());
@@ -1,370 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Techcable <Techcable@outlook.com>
Date: Thu, 3 Mar 2016 13:20:33 -0700
Subject: [PATCH] Use ASM for event executors.
Uses method handles for private or static methods.
diff --git a/build.gradle.kts b/build.gradle.kts
index cbab63d2f3b045f6193f5a3422ae4b5509d3003b..ba6f0a70ba2442dbe60ed6cc92e4fb91a48d9f3b 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -42,6 +42,9 @@ dependencies {
apiAndDocs("net.kyori:adventure-text-serializer-legacy")
apiAndDocs("net.kyori:adventure-text-serializer-plain")
apiAndDocs("net.kyori:adventure-text-logger-slf4j")
+
+ 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")
diff --git a/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java b/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b28e9b1daba7834af67dbc193dd656bedd9a994
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java
@@ -0,0 +1,42 @@
+package com.destroystokyo.paper.event.executor;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+
+import com.destroystokyo.paper.util.SneakyThrow;
+import org.bukkit.event.Event;
+import org.bukkit.event.EventException;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.EventExecutor;
+import org.jetbrains.annotations.NotNull;
+
+public class MethodHandleEventExecutor implements EventExecutor {
+ private final Class<? extends Event> eventClass;
+ private final MethodHandle handle;
+
+ public MethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull MethodHandle handle) {
+ this.eventClass = eventClass;
+ this.handle = handle;
+ }
+
+ public MethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull Method m) {
+ this.eventClass = eventClass;
+ try {
+ m.setAccessible(true);
+ this.handle = MethodHandles.lookup().unreflect(m);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError("Unable to set accessible", e);
+ }
+ }
+
+ @Override
+ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
+ if (!eventClass.isInstance(event)) return;
+ try {
+ handle.invoke(listener, event);
+ } catch (Throwable t) {
+ SneakyThrow.sneaky(t);
+ }
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java b/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..c83672427324bd068ed52916f700b68446a226f6
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java
@@ -0,0 +1,43 @@
+package com.destroystokyo.paper.event.executor;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import com.destroystokyo.paper.util.SneakyThrow;
+import com.google.common.base.Preconditions;
+
+import org.bukkit.Bukkit;
+import org.bukkit.event.Event;
+import org.bukkit.event.EventException;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.EventExecutor;
+import org.jetbrains.annotations.NotNull;
+
+public class StaticMethodHandleEventExecutor implements EventExecutor {
+ private final Class<? extends Event> eventClass;
+ private final MethodHandle handle;
+
+ public StaticMethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull Method m) {
+ Preconditions.checkArgument(Modifier.isStatic(m.getModifiers()), "Not a static method: %s", m);
+ Preconditions.checkArgument(eventClass != null, "eventClass is null");
+ this.eventClass = eventClass;
+ try {
+ m.setAccessible(true);
+ this.handle = MethodHandles.lookup().unreflect(m);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError("Unable to set accessible", e);
+ }
+ }
+
+ @Override
+ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
+ if (!eventClass.isInstance(event)) return;
+ try {
+ handle.invoke(event);
+ } catch (Throwable throwable) {
+ SneakyThrow.sneaky(throwable);
+ }
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java b/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..084c31af1a7ba32bb4c3dc8f16f67fd09ce0b6a4
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java
@@ -0,0 +1,54 @@
+package com.destroystokyo.paper.event.executor.asm;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.bukkit.plugin.EventExecutor;
+import org.jetbrains.annotations.NotNull;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.GeneratorAdapter;
+
+import static org.objectweb.asm.Opcodes.*;
+
+public class ASMEventExecutorGenerator {
+
+ private static final String EXECUTE_DESCRIPTOR = "(Lorg/bukkit/event/Listener;Lorg/bukkit/event/Event;)V";
+
+ @NotNull
+ public static byte[] generateEventExecutor(@NotNull Method m, @NotNull String name) {
+ ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ writer.visit(V1_8, ACC_PUBLIC, name.replace('.', '/'), null, Type.getInternalName(Object.class), new String[] {Type.getInternalName(EventExecutor.class)});
+ // Generate constructor
+ GeneratorAdapter methodGenerator = new GeneratorAdapter(writer.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null), ACC_PUBLIC, "<init>", "()V");
+ methodGenerator.loadThis();
+ methodGenerator.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V", false); // Invoke the super class (Object) constructor
+ methodGenerator.returnValue();
+ methodGenerator.endMethod();
+ // Generate the execute method
+ methodGenerator = new GeneratorAdapter(writer.visitMethod(ACC_PUBLIC, "execute", EXECUTE_DESCRIPTOR, null, null), ACC_PUBLIC, "execute", EXECUTE_DESCRIPTOR);
+ methodGenerator.loadArg(0);
+ methodGenerator.checkCast(Type.getType(m.getDeclaringClass()));
+ methodGenerator.loadArg(1);
+ methodGenerator.checkCast(Type.getType(m.getParameterTypes()[0]));
+ methodGenerator.visitMethodInsn(m.getDeclaringClass().isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, Type.getInternalName(m.getDeclaringClass()), m.getName(), Type.getMethodDescriptor(m), m.getDeclaringClass().isInterface());
+ // The only purpose of this switch statement is to generate the correct pop instruction, should the event handler method return something other than void.
+ // Non-void event handlers will be unsupported in a future release.
+ switch (Type.getType(m.getReturnType()).getSize()) {
+ // case 0 is omitted because the only type that has size 0 is void - no pop instruction needed.
+ case 1 -> methodGenerator.pop(); // handles reference types and most primitives
+ case 2 -> methodGenerator.pop2(); // handles long and double
+ }
+ methodGenerator.returnValue();
+ methodGenerator.endMethod();
+ writer.visitEnd();
+ return writer.toByteArray();
+ }
+
+ public static AtomicInteger NEXT_ID = new AtomicInteger(1);
+ @NotNull
+ public static String generateName() {
+ int id = NEXT_ID.getAndIncrement();
+ return "com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor" + id;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java b/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java
new file mode 100644
index 0000000000000000000000000000000000000000..f79685b48bb581277a6891927988b6f7a4389dc4
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java
@@ -0,0 +1,34 @@
+package com.destroystokyo.paper.event.executor.asm;
+
+import org.jetbrains.annotations.NotNull;
+
+public interface ClassDefiner {
+
+ /**
+ * Returns if the defined classes can bypass access checks
+ *
+ * @return if classes bypass access checks
+ */
+ public default boolean isBypassAccessChecks() {
+ return false;
+ }
+
+ /**
+ * Define a class
+ *
+ * @param parentLoader the parent classloader
+ * @param name the name of the class
+ * @param data the class data to load
+ * @return the defined class
+ * @throws ClassFormatError if the class data is invalid
+ * @throws NullPointerException if any of the arguments are null
+ */
+ @NotNull
+ public Class<?> defineClass(@NotNull ClassLoader parentLoader, @NotNull String name, @NotNull byte[] data);
+
+ @NotNull
+ public static ClassDefiner getInstance() {
+ return SafeClassDefiner.INSTANCE;
+ }
+
+}
diff --git a/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java b/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac99477e9f2c08041aeff31abc1d1edee58d0a67
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java
@@ -0,0 +1,66 @@
+package com.destroystokyo.paper.event.executor.asm;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.google.common.base.Preconditions;
+
+import com.google.common.collect.MapMaker;
+import org.jetbrains.annotations.NotNull;
+import org.objectweb.asm.Type;
+
+public class SafeClassDefiner implements ClassDefiner {
+ /* default */ static final SafeClassDefiner INSTANCE = new SafeClassDefiner();
+
+ private SafeClassDefiner() {}
+
+ private final ConcurrentMap<ClassLoader, GeneratedClassLoader> loaders = new MapMaker().weakKeys().makeMap();
+
+ @NotNull
+ @Override
+ public Class<?> defineClass(@NotNull ClassLoader parentLoader, @NotNull String name, @NotNull byte[] data) {
+ GeneratedClassLoader loader = loaders.computeIfAbsent(parentLoader, GeneratedClassLoader::new);
+ synchronized (loader.getClassLoadingLock(name)) {
+ Preconditions.checkState(!loader.hasClass(name), "%s already defined", name);
+ Class<?> c = loader.define(name, data);
+ assert c.getName().equals(name);
+ return c;
+ }
+ }
+
+ private static class GeneratedClassLoader extends ClassLoader {
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
+
+ protected GeneratedClassLoader(@NotNull ClassLoader parent) {
+ super(parent);
+ }
+
+ private Class<?> define(@NotNull String name, byte[] data) {
+ synchronized (getClassLoadingLock(name)) {
+ assert !hasClass(name);
+ Class<?> c = defineClass(name, data, 0, data.length);
+ resolveClass(c);
+ return c;
+ }
+ }
+
+ @Override
+ @NotNull
+ public Object getClassLoadingLock(@NotNull String name) {
+ return super.getClassLoadingLock(name);
+ }
+
+ public boolean hasClass(@NotNull String name) {
+ synchronized (getClassLoadingLock(name)) {
+ try {
+ Class.forName(name);
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/plugin/EventExecutor.java b/src/main/java/org/bukkit/plugin/EventExecutor.java
index a850f0780de05463fc0d3f9e15ff7f19d88b2aed..9026e108ccd3a88aee1267ee275137befa646455 100644
--- a/src/main/java/org/bukkit/plugin/EventExecutor.java
+++ b/src/main/java/org/bukkit/plugin/EventExecutor.java
@@ -5,9 +5,75 @@ import org.bukkit.event.EventException;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
+// Paper start
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+
+import com.destroystokyo.paper.event.executor.MethodHandleEventExecutor;
+import com.destroystokyo.paper.event.executor.StaticMethodHandleEventExecutor;
+import com.destroystokyo.paper.event.executor.asm.ASMEventExecutorGenerator;
+import com.destroystokyo.paper.event.executor.asm.ClassDefiner;
+import com.google.common.base.Preconditions;
+// Paper end
+
/**
* Interface which defines the class for event call backs to plugins
*/
public interface EventExecutor {
public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException;
+
+ // Paper start
+ ConcurrentMap<Method, Class<? extends EventExecutor>> eventExecutorMap = new ConcurrentHashMap<Method, Class<? extends EventExecutor>>() {
+ @NotNull
+ @Override
+ public Class<? extends EventExecutor> computeIfAbsent(@NotNull Method key, @NotNull Function<? super Method, ? extends Class<? extends EventExecutor>> mappingFunction) {
+ Class<? extends EventExecutor> executorClass = get(key);
+ if (executorClass != null)
+ return executorClass;
+
+ //noinspection SynchronizationOnLocalVariableOrMethodParameter
+ synchronized (key) {
+ executorClass = get(key);
+ if (executorClass != null)
+ return executorClass;
+
+ return super.computeIfAbsent(key, mappingFunction);
+ }
+ }
+ };
+
+ @NotNull
+ public static EventExecutor create(@NotNull Method m, @NotNull Class<? extends Event> eventClass) {
+ Preconditions.checkNotNull(m, "Null method");
+ Preconditions.checkArgument(m.getParameterCount() != 0, "Incorrect number of arguments %s", m.getParameterCount());
+ Preconditions.checkArgument(m.getParameterTypes()[0] == eventClass, "First parameter %s doesn't match event class %s", m.getParameterTypes()[0], eventClass);
+ ClassDefiner definer = ClassDefiner.getInstance();
+ if (Modifier.isStatic(m.getModifiers())) {
+ return new StaticMethodHandleEventExecutor(eventClass, m);
+ } else if (definer.isBypassAccessChecks() || Modifier.isPublic(m.getDeclaringClass().getModifiers()) && Modifier.isPublic(m.getModifiers())) {
+ // get the existing generated EventExecutor class for the Method or generate one
+ Class<? extends EventExecutor> executorClass = eventExecutorMap.computeIfAbsent(m, (__) -> {
+ String name = ASMEventExecutorGenerator.generateName();
+ byte[] classData = ASMEventExecutorGenerator.generateEventExecutor(m, name);
+ return definer.defineClass(m.getDeclaringClass().getClassLoader(), name, classData).asSubclass(EventExecutor.class);
+ });
+
+ try {
+ EventExecutor asmExecutor = executorClass.newInstance();
+ // Define a wrapper to conform to bukkit stupidity (passing in events that don't match and wrapper exception)
+ return (listener, event) -> {
+ if (!eventClass.isInstance(event)) return;
+ asmExecutor.execute(listener, event);
+ };
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new AssertionError("Unable to initialize generated event executor", e);
+ }
+ } else {
+ return new MethodHandleEventExecutor(eventClass, m);
+ }
+ }
+ // Paper end
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-422
View File
@@ -1,422 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 20 Mar 2022 10:42:28 -0700
Subject: [PATCH] Add Position
diff --git a/src/main/java/io/papermc/paper/math/BlockPosition.java b/src/main/java/io/papermc/paper/math/BlockPosition.java
new file mode 100644
index 0000000000000000000000000000000000000000..f0fdabce4af640da2a406412e67020761ded3ac1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/math/BlockPosition.java
@@ -0,0 +1,98 @@
+package io.papermc.paper.math;
+
+import org.bukkit.Axis;
+import org.bukkit.block.BlockFace;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A position represented with integers.
+ * <p>
+ * <b>May see breaking changes until Experimental annotation is removed.</b>
+ * @see FinePosition
+ */
+@ApiStatus.Experimental
+public interface BlockPosition extends Position {
+
+ @Override
+ default double x() {
+ return this.blockX();
+ }
+
+ @Override
+ default double y() {
+ return this.blockY();
+ }
+
+ @Override
+ default double z() {
+ return this.blockZ();
+ }
+
+ @Override
+ default boolean isBlock() {
+ return true;
+ }
+
+ @Override
+ default boolean isFine() {
+ return false;
+ }
+
+ @Override
+ default @NotNull BlockPosition toBlock() {
+ return this;
+ }
+
+ @Override
+ default @NotNull BlockPosition offset(int x, int y, int z) {
+ return x == 0 && y == 0 && z == 0 ? this : new BlockPositionImpl(this.blockX() + x, this.blockY() + y, this.blockZ() + z);
+ }
+
+ @Override
+ default @NotNull FinePosition offset(double x, double y, double z) {
+ return new FinePositionImpl(this.blockX() + x, this.blockY() + y, this.blockZ() + z);
+ }
+
+ /**
+ * Returns a block position offset by 1 in the direction specified.
+ *
+ * @param blockFace the block face to offset towards
+ * @return the offset block position
+ */
+ @Contract(value = "_ -> new", pure = true)
+ default @NotNull BlockPosition offset(@NotNull BlockFace blockFace) {
+ return this.offset(blockFace, 1);
+ }
+
+ /**
+ * Returns a block position offset in the direction specified
+ * multiplied by the amount.
+ *
+ * @param blockFace the block face to offset towards
+ * @param amount the number of times to move in that direction
+ * @return the offset block position
+ */
+ @Contract(pure = true)
+ default @NotNull BlockPosition offset(@NotNull BlockFace blockFace, int amount) {
+ return amount == 0 ? this : new BlockPositionImpl(this.blockX() + (blockFace.getModX() * amount), this.blockY() + (blockFace.getModY() * amount), this.blockZ() + (blockFace.getModZ() * amount));
+ }
+
+ /**
+ * Returns a block position offset by the amount along
+ * the specified axis.
+ *
+ * @param axis the axis to offset along
+ * @param amount the amount to offset along that axis
+ * @return the offset block position
+ */
+ @Contract(pure = true)
+ default @NotNull BlockPosition offset(@NotNull Axis axis, int amount) {
+ return amount == 0 ? this : switch (axis) {
+ case X -> new BlockPositionImpl(this.blockX() + amount, this.blockY(), this.blockZ());
+ case Y -> new BlockPositionImpl(this.blockX(), this.blockY() + amount, this.blockZ());
+ case Z -> new BlockPositionImpl(this.blockX(), this.blockY(), this.blockZ() + amount);
+ };
+ }
+}
diff --git a/src/main/java/io/papermc/paper/math/BlockPositionImpl.java b/src/main/java/io/papermc/paper/math/BlockPositionImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb5a3f26c7ba56c6715827f52c0013a860ec7d9a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/math/BlockPositionImpl.java
@@ -0,0 +1,4 @@
+package io.papermc.paper.math;
+
+record BlockPositionImpl(int blockX, int blockY, int blockZ) implements BlockPosition {
+}
diff --git a/src/main/java/io/papermc/paper/math/FinePosition.java b/src/main/java/io/papermc/paper/math/FinePosition.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8df70d731573cf2446044925f218876d62fd7cf
--- /dev/null
+++ b/src/main/java/io/papermc/paper/math/FinePosition.java
@@ -0,0 +1,56 @@
+package io.papermc.paper.math;
+
+import org.bukkit.util.NumberConversions;
+import org.bukkit.util.Vector;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A position represented with doubles.
+ * <p>
+ * <b>May see breaking changes until Experimental annotation is removed.</b>
+ * @see BlockPosition
+ */
+@ApiStatus.Experimental
+public interface FinePosition extends Position {
+
+ @Override
+ default int blockX() {
+ return NumberConversions.floor(this.x());
+ }
+
+ @Override
+ default int blockY() {
+ return NumberConversions.floor(this.y());
+ }
+
+ @Override
+ default int blockZ() {
+ return NumberConversions.floor(this.z());
+ }
+
+ @Override
+ default boolean isBlock() {
+ return false;
+ }
+
+ @Override
+ default boolean isFine() {
+ return true;
+ }
+
+ @Override
+ default @NotNull BlockPosition toBlock() {
+ return new BlockPositionImpl(this.blockX(), this.blockY(), this.blockZ());
+ }
+
+ @Override
+ default @NotNull FinePosition offset(int x, int y, int z) {
+ return this.offset((double) x, y, z);
+ }
+
+ @Override
+ default @NotNull FinePosition offset(double x, double y, double z) {
+ return x == 0.0 && y == 0.0 && z == 0.0 ? this : new FinePositionImpl(this.x() + x, this.y() + y, this.z() + z);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/math/FinePositionImpl.java b/src/main/java/io/papermc/paper/math/FinePositionImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..93476aaf8d21efb5a30b6d2cc2eeda8100fb72d0
--- /dev/null
+++ b/src/main/java/io/papermc/paper/math/FinePositionImpl.java
@@ -0,0 +1,4 @@
+package io.papermc.paper.math;
+
+record FinePositionImpl(double x, double y, double z) implements FinePosition {
+}
diff --git a/src/main/java/io/papermc/paper/math/Position.java b/src/main/java/io/papermc/paper/math/Position.java
new file mode 100644
index 0000000000000000000000000000000000000000..300da713dcc303b340efad70efe57facf5422964
--- /dev/null
+++ b/src/main/java/io/papermc/paper/math/Position.java
@@ -0,0 +1,184 @@
+package io.papermc.paper.math;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.util.Vector;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Common interface for {@link FinePosition} and {@link BlockPosition}.
+ * <p>
+ * <b>May see breaking changes until Experimental annotation is removed.</b>
+ */
+@ApiStatus.Experimental
+public interface Position {
+
+ FinePosition FINE_ZERO = new FinePositionImpl(0, 0, 0);
+ BlockPosition BLOCK_ZERO = new BlockPositionImpl(0, 0, 0);
+
+ /**
+ * Gets the block x value for this position
+ *
+ * @return the block x value
+ */
+ int blockX();
+
+ /**
+ * Gets the block x value for this position
+ *
+ * @return the block x value
+ */
+ int blockY();
+
+ /**
+ * Gets the block x value for this position
+ *
+ * @return the block x value
+ */
+ int blockZ();
+
+ /**
+ * Gets the x value for this position
+ *
+ * @return the x value
+ */
+ double x();
+
+ /**
+ * Gets the y value for this position
+ *
+ * @return the y value
+ */
+ double y();
+
+ /**
+ * Gets the z value for this position
+ *
+ * @return the z value
+ */
+ double z();
+
+ /**
+ * Checks of this position represents a {@link BlockPosition}
+ *
+ * @return true if block
+ */
+ boolean isBlock();
+
+ /**
+ * Checks if this position represents a {@link FinePosition}
+ *
+ * @return true if fine
+ */
+ boolean isFine();
+
+ /**
+ * Returns a position offset by the specified amounts.
+ *
+ * @param x x value to offset
+ * @param y y value to offset
+ * @param z z value to offset
+ * @return the offset position
+ */
+ @NotNull Position offset(int x, int y, int z);
+
+ /**
+ * Returns a position offset by the specified amounts.
+ *
+ * @param x x value to offset
+ * @param y y value to offset
+ * @param z z value to offset
+ * @return the offset position
+ */
+ @NotNull FinePosition offset(double x, double y, double z);
+
+ /**
+ * Returns a new position at the center of the block position this represents
+ *
+ * @return a new center position
+ */
+ @Contract(value = "-> new", pure = true)
+ default @NotNull FinePosition toCenter() {
+ return new FinePositionImpl(this.blockX() + 0.5, this.blockY() + 0.5, this.blockZ() + 0.5);
+ }
+
+ /**
+ * Returns the block position of this position
+ * or itself if it already is a block position
+ *
+ * @return the block position
+ */
+ @Contract(pure = true)
+ @NotNull BlockPosition toBlock();
+
+ /**
+ * Converts this position to a vector
+ *
+ * @return a new vector
+ */
+ @Contract(value = "-> new", pure = true)
+ default @NotNull Vector toVector() {
+ return new Vector(this.x(), this.y(), this.z());
+ }
+
+ /**
+ * Creates a new location object at this position with the specified world
+ *
+ * @param world the world for the location object
+ * @return a new location
+ */
+ @Contract(value = "_ -> new", pure = true)
+ default @NotNull Location toLocation(@NotNull World world) {
+ return new Location(world, this.x(), this.y(), this.z());
+ }
+
+ /**
+ * Creates a position at the coordinates
+ *
+ * @param x x coord
+ * @param y y coord
+ * @param z z coord
+ * @return a position with those coords
+ */
+ @Contract(value = "_, _, _ -> new", pure = true)
+ static @NotNull BlockPosition block(int x, int y, int z) {
+ return new BlockPositionImpl(x, y, z);
+ }
+
+ /**
+ * Creates a position from the location.
+ *
+ * @param location the location to copy the position of
+ * @return a new position at that location
+ */
+ @Contract(value = "_ -> new", pure = true)
+ static @NotNull BlockPosition block(@NotNull Location location) {
+ return new BlockPositionImpl(location.getBlockX(), location.getBlockY(), location.getBlockZ());
+ }
+
+ /**
+ * Creates a position at the coordinates
+ *
+ * @param x x coord
+ * @param y y coord
+ * @param z z coord
+ * @return a position with those coords
+ */
+ @Contract(value = "_, _, _ -> new", pure = true)
+ static @NotNull FinePosition fine(double x, double y, double z) {
+ return new FinePositionImpl(x, y, z);
+ }
+
+ /**
+ * Creates a position from the location.
+ *
+ * @param location the location to copy the position of
+ * @return a new position at that location
+ */
+ @Contract(value = "_ -> new", pure = true)
+ static @NotNull FinePosition fine(@NotNull Location location) {
+ return new FinePositionImpl(location.getX(), location.getY(), location.getZ());
+ }
+}
diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
index 7c4db051472fb6a6c6d24092dc6f75487356690a..3b99f359f556e6f2c341d55fa69b7462e69b6546 100644
--- a/src/main/java/org/bukkit/Location.java
+++ b/src/main/java/org/bukkit/Location.java
@@ -20,7 +20,7 @@ import org.jetbrains.annotations.Nullable;
* magnitude than 360 are valid, but may be normalized to any other equivalent
* representation by the implementation.
*/
-public class Location implements Cloneable, ConfigurationSerializable {
+public class Location implements Cloneable, ConfigurationSerializable, io.papermc.paper.math.FinePosition { // Paper
private Reference<World> world;
private double x;
private double y;
@@ -706,4 +706,26 @@ public class Location implements Cloneable, ConfigurationSerializable {
}
return pitch;
}
+
+ // Paper - add Position
+ @Override
+ public double x() {
+ return this.getX();
+ }
+
+ @Override
+ public double y() {
+ return this.getY();
+ }
+
+ @Override
+ public double z() {
+ return this.getZ();
+ }
+
+ @Override
+ public @NotNull Location toLocation(@NotNull World world) {
+ return new Location(world, this.x(), this.y(), this.z(), this.getYaw(), this.getPitch());
+ }
+ // Paper end
}
@@ -0,0 +1,33 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jedediah Smith <jedediah@silencegreys.com>
Date: Mon, 29 Feb 2016 17:22:34 -0600
Subject: [PATCH] Player affects spawning API
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index e3cc40a54b48e8a90ccc93c272c1a2dca1da9595..a6f6dea965eee2de2183f189580d1b7141a9308f 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -1746,6 +1746,22 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@Deprecated // Paper
public String getLocale();
+ // Paper start
+ /**
+ * Get whether the player can affect mob spawning
+ *
+ * @return if the player can affect mob spawning
+ */
+ public boolean getAffectsSpawning();
+
+ /**
+ * Set whether the player can affect mob spawning
+ *
+ * @param affects Whether the player can affect mob spawning
+ */
+ public void setAffectsSpawning(boolean affects);
+ // Paper end
+
/**
* Update the list of commands sent to the client.
* <br>
+49
View File
@@ -0,0 +1,49 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Mon, 29 Feb 2016 17:24:57 -0600
Subject: [PATCH] Add getTPS method
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index 84a620bbbc24ded4075bce0209caed7fa03a92eb..bbee4e40917cca4e692c3d60c1c513b4c847160f 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -1728,6 +1728,17 @@ public final class Bukkit {
return server.getEntity(uuid);
}
+ // Paper start
+ /**
+ * Gets the current server TPS
+ * @return current server TPS (1m, 5m, 15m in Paper-Server)
+ */
+ @NotNull
+ public static double[] getTPS() {
+ return server.getTPS();
+ }
+ // Paper end
+
/**
* Get the advancement specified by this key.
*
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index ede5de4c310a93989608bf48b3e5116f380589dd..835041d2ab6545e52d963d96d558f9a7e52d279a 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -1461,6 +1461,16 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
@Nullable
Entity getEntity(@NotNull UUID uuid);
+ // Paper start
+ /**
+ * Gets the current server TPS
+ *
+ * @return current server TPS (1m, 5m, 15m in Paper-Server)
+ */
+ @NotNull
+ public double[] getTPS();
+ // Paper end
+
/**
* Get the advancement specified by this key.
*
File diff suppressed because it is too large. Load diff
@@ -1,96 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Tue, 18 May 2021 14:42:26 -0700
Subject: [PATCH] Add command line option to load extra plugin jars not in the
plugins folder
ex: java -jar paperclip.jar nogui -add-plugin=/path/to/plugin.jar -add-plugin=/path/to/another/plugin_jar.jar
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index 45f9ac35874c67c24718f6891b95df1ff4f0b93f..7717f5346aa784df4a0f6e43a5a74a66f279bc8a 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -78,6 +78,20 @@ public final class Bukkit {
return server;
}
+ /**
+ * Returns the de facto plugins directory, generally used for storing plugin jars to be loaded,
+ * as well as their {@link org.bukkit.plugin.Plugin#getDataFolder() data folders}.
+ *
+ * <p>Plugins should use {@link org.bukkit.plugin.Plugin#getDataFolder()} rather than traversing this
+ * directory manually when determining the location in which to store their data and configuration files.</p>
+ *
+ * @return plugins directory
+ */
+ @NotNull
+ public static File getPluginsFolder() {
+ return server.getPluginsFolder();
+ }
+
/**
* Attempts to set the {@link Server} singleton.
* <p>
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 087da3cd2cafd8255ed70e5e632b270a33e37eaf..0f990d33ab07249e4cf2d2269920b4cbeed27d39 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -63,6 +63,18 @@ import org.jetbrains.annotations.Nullable;
*/
public interface Server extends PluginMessageRecipient, net.kyori.adventure.audience.ForwardingAudience { // Paper
+ /**
+ * Returns the de facto plugins directory, generally used for storing plugin jars to be loaded,
+ * as well as their {@link org.bukkit.plugin.Plugin#getDataFolder() data folders}.
+ *
+ * <p>Plugins should use {@link org.bukkit.plugin.Plugin#getDataFolder()} rather than traversing this
+ * directory manually when determining the location in which to store their data and configuration files.</p>
+ *
+ * @return plugins directory
+ */
+ @NotNull
+ File getPluginsFolder();
+
/**
* Used for all administrative messages, such as an operator using a
* command.
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index 63389474a2b3f0e283b42e7004aa6a94904a3d17..974ff7116b294473ec450757e8a9341540f43dcd 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -116,9 +116,22 @@ public final class SimplePluginManager implements PluginManager {
@Override
@NotNull
public Plugin[] loadPlugins(@NotNull File directory) {
+ // Paper start - extra jars
+ return this.loadPlugins(directory, java.util.Collections.emptyList());
+ }
+ @NotNull
+ public Plugin[] loadPlugins(final @NotNull File directory, final @NotNull List<File> extraPluginJars) {
+ // Paper end
if (true) {
List<Plugin> pluginList = new ArrayList<>();
java.util.Collections.addAll(pluginList, this.paperPluginManager.loadPlugins(directory));
+ for (File file : extraPluginJars) {
+ try {
+ pluginList.add(this.paperPluginManager.loadPlugin(file));
+ } catch (Exception e) {
+ this.server.getLogger().log(Level.SEVERE, "Plugin loading error!", e);
+ }
+ }
return pluginList.toArray(new Plugin[0]);
}
Preconditions.checkArgument(directory != null, "Directory cannot be null");
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
index de6b6188587fd254ab96cbc1f95fda2056d9cbe2..eaefbb00e9993d54906cc8cf35cf753c0d6c7707 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
@@ -93,7 +93,7 @@ public final class JavaPluginLoader implements PluginLoader {
throw new InvalidPluginException(ex);
}
- final File parentFile = file.getParentFile();
+ final File parentFile = this.server.getPluginsFolder(); // Paper
final File dataFolder = new File(parentFile, description.getName());
@SuppressWarnings("deprecation")
final File oldDataFolder = new File(parentFile, description.getRawName());
+185
View File
@@ -0,0 +1,185 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach@zachbr.io>
Date: Mon, 27 May 2019 01:10:06 -0500
Subject: [PATCH] Version Command 2.0
diff --git a/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..a736d7bcdc5861a01b66ba36158db1c716339346
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
@@ -0,0 +1,45 @@
+package com.destroystokyo.paper.util;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.Bukkit;
+import org.jetbrains.annotations.NotNull;
+
+public interface VersionFetcher {
+ /**
+ * Amount of time to cache results for in milliseconds
+ * <p>
+ * Negative values will never cache.
+ *
+ * @return cache time
+ */
+ long getCacheTime();
+
+ /**
+ * Gets the version message to cache and show to command senders.
+ *
+ * <p>NOTE: This is run in a new thread separate from that of the command processing thread</p>
+ *
+ * @param serverVersion the current version of the server (will match {@link Bukkit#getVersion()})
+ * @return the message to show when requesting a version
+ */
+ @NotNull
+ Component getVersionMessage(@NotNull String serverVersion);
+
+ class DummyVersionFetcher implements VersionFetcher {
+
+ @Override
+ public long getCacheTime() {
+ return -1;
+ }
+
+ @NotNull
+ @Override
+ public Component getVersionMessage(@NotNull String serverVersion) {
+ Bukkit.getLogger().warning("Version provider has not been set, cannot check for updates!");
+ Bukkit.getLogger().info("Override the default implementation of org.bukkit.UnsafeValues#getVersionFetcher()");
+ new Throwable().printStackTrace();
+ return Component.text("Unable to check for updates. No version provider set.", NamedTextColor.RED);
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
index e464769f72bf7d8e0f0630fd4f03011f8ac80dc7..bfeec56e83229dbfc8625b3b90bd6c138c73bc1d 100644
--- a/src/main/java/org/bukkit/UnsafeValues.java
+++ b/src/main/java/org/bukkit/UnsafeValues.java
@@ -85,5 +85,12 @@ public interface UnsafeValues {
* @return name
*/
String getTimingsServerName();
+
+ /**
+ * Called once by the version command on first use, then cached.
+ */
+ default com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
+ return new com.destroystokyo.paper.util.VersionFetcher.DummyVersionFetcher();
+ }
// Paper end
}
diff --git a/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/src/main/java/org/bukkit/command/defaults/VersionCommand.java
index 2305eb40832a82159cd89162934870cf57e1aa0e..4c2ddc722a9dc4011906ad9530b13fa9be1d3ff9 100644
--- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java
+++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java
@@ -1,5 +1,6 @@
package org.bukkit.command.defaults;
+import com.destroystokyo.paper.util.VersionFetcher; // Paper - version supplier
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Resources;
@@ -16,6 +17,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
+import net.kyori.adventure.text.Component; // Paper
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
@@ -26,6 +28,15 @@ import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
public class VersionCommand extends BukkitCommand {
+ private VersionFetcher versionFetcher;
+ private VersionFetcher getVersionFetcher() { // lazy load because unsafe isn't available at command registration
+ if (versionFetcher == null) {
+ versionFetcher = Bukkit.getUnsafe().getVersionFetcher();
+ }
+
+ return versionFetcher;
+ }
+
public VersionCommand(@NotNull String name) {
super(name);
@@ -40,7 +51,7 @@ public class VersionCommand extends BukkitCommand {
if (!testPermission(sender)) return true;
if (args.length == 0) {
- sender.sendMessage("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")");
+ //sender.sendMessage("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")"); // Paper - moved to setVersionMessage
sendVersion(sender);
} else {
StringBuilder name = new StringBuilder();
@@ -146,14 +157,14 @@ public class VersionCommand extends BukkitCommand {
private final ReentrantLock versionLock = new ReentrantLock();
private boolean hasVersion = false;
- private String versionMessage = null;
+ private Component versionMessage = null; // Paper
private final Set<CommandSender> versionWaiters = new HashSet<CommandSender>();
private boolean versionTaskStarted = false;
private long lastCheck = 0;
private void sendVersion(@NotNull CommandSender sender) {
if (hasVersion) {
- if (System.currentTimeMillis() - lastCheck > 21600000) {
+ if (System.currentTimeMillis() - lastCheck > getVersionFetcher().getCacheTime()) { // Paper - use version supplier
lastCheck = System.currentTimeMillis();
hasVersion = false;
} else {
@@ -168,7 +179,7 @@ public class VersionCommand extends BukkitCommand {
return;
}
versionWaiters.add(sender);
- sender.sendMessage("Checking version, please wait...");
+ sender.sendMessage(Component.text("Checking version, please wait...", net.kyori.adventure.text.format.NamedTextColor.WHITE, net.kyori.adventure.text.format.TextDecoration.ITALIC)); // Paper
if (!versionTaskStarted) {
versionTaskStarted = true;
new Thread(new Runnable() {
@@ -186,6 +197,13 @@ public class VersionCommand extends BukkitCommand {
private void obtainVersion() {
String version = Bukkit.getVersion();
+ // Paper start
+ if (version.startsWith("null")) { // running from ide?
+ setVersionMessage(Component.text("Unknown version, custom build?", net.kyori.adventure.text.format.NamedTextColor.YELLOW));
+ return;
+ }
+ setVersionMessage(getVersionFetcher().getVersionMessage(version));
+ /*
if (version == null) version = "Custom";
String[] parts = version.substring(0, version.indexOf(' ')).split("-");
if (parts.length == 4) {
@@ -215,11 +233,24 @@ public class VersionCommand extends BukkitCommand {
} else {
setVersionMessage("Unknown version, custom build?");
}
+ */
+ // Paper end
}
- private void setVersionMessage(@NotNull String msg) {
+ // Paper start
+ private void setVersionMessage(final @NotNull Component msg) {
lastCheck = System.currentTimeMillis();
- versionMessage = msg;
+ final Component message = net.kyori.adventure.text.TextComponent.ofChildren(
+ Component.text("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")", net.kyori.adventure.text.format.NamedTextColor.WHITE),
+ Component.newline(),
+ msg
+ );
+ this.versionMessage = Component.text()
+ .append(message)
+ .hoverEvent(Component.text("Click to copy to clipboard", net.kyori.adventure.text.format.NamedTextColor.WHITE))
+ .clickEvent(net.kyori.adventure.text.event.ClickEvent.copyToClipboard(net.kyori.adventure.text.serializer.plain.PlainComponentSerializer.plain().serialize(message)))
+ .build();
+ // Paper end
versionLock.lock();
try {
hasVersion = true;
+66
View File
@@ -0,0 +1,66 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Byteflux <byte@byteflux.net>
Date: Mon, 29 Feb 2016 17:50:31 -0600
Subject: [PATCH] Entity Origin API
diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
index 10cb6088c4618f228c757f4e592b44edab81c2dc..65088203d29798efe211612dfadb356f457ed466 100644
--- a/src/main/java/org/bukkit/entity/Entity.java
+++ b/src/main/java/org/bukkit/entity/Entity.java
@@ -663,5 +663,15 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.customName())));
}
+
+ /**
+ * Gets the location where this entity originates from.
+ * <p>
+ * This value can be null if the entity hasn't yet been added to the world.
+ *
+ * @return Location where entity originates or null if not yet added
+ */
+ @Nullable
+ Location getOrigin();
// Paper end
}
diff --git a/src/main/java/org/bukkit/entity/FallingBlock.java b/src/main/java/org/bukkit/entity/FallingBlock.java
index 64f9d3fd870d65afd2ee9a85625b149163eee144..14cb0d770561151570ab4399ca5facff43076819 100644
--- a/src/main/java/org/bukkit/entity/FallingBlock.java
+++ b/src/main/java/org/bukkit/entity/FallingBlock.java
@@ -54,4 +54,15 @@ public interface FallingBlock extends Entity {
* @param hurtEntities whether entities will be damaged by this block.
*/
void setHurtEntities(boolean hurtEntities);
+
+ /**
+ * Gets the source block location of the FallingBlock
+ *
+ * @return the source block location the FallingBlock was spawned from
+ * @deprecated replaced by {@link Entity#getOrigin()}
+ */
+ @Deprecated
+ default org.bukkit.Location getSourceLoc() {
+ return this.getOrigin();
+ }
}
diff --git a/src/main/java/org/bukkit/entity/TNTPrimed.java b/src/main/java/org/bukkit/entity/TNTPrimed.java
index a23cfdf66877f0a61eae700de084c76e6ee7b431..0813bd913c8fdb2001963ce3e82c07c2af105418 100644
--- a/src/main/java/org/bukkit/entity/TNTPrimed.java
+++ b/src/main/java/org/bukkit/entity/TNTPrimed.java
@@ -53,4 +53,15 @@ public interface TNTPrimed extends Explosive {
* @param source the source of this primed TNT
*/
public void setSource(@Nullable Entity source);
+
+ /**
+ * Gets the source block location of the TNTPrimed
+ *
+ * @return the source block location the TNTPrimed was spawned from
+ * @deprecated replaced by {@link Entity#getOrigin()}
+ */
+ @Deprecated
+ default org.bukkit.Location getSourceLoc() {
+ return this.getOrigin();
+ }
}
@@ -1,33 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jedediah Smith <jedediah@silencegreys.com>
Date: Mon, 29 Feb 2016 17:22:34 -0600
Subject: [PATCH] Player affects spawning API
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 1a54682f584479a5a548f6f1f2ffd5da5dc11c88..6b1a198f930a5a995e0cb6c279b455e1639a5039 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -2196,6 +2196,22 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@Deprecated // Paper
public String getLocale();
+ // Paper start
+ /**
+ * Get whether the player can affect mob spawning
+ *
+ * @return if the player can affect mob spawning
+ */
+ public boolean getAffectsSpawning();
+
+ /**
+ * Set whether the player can affect mob spawning
+ *
+ * @param affects Whether the player can affect mob spawning
+ */
+ public void setAffectsSpawning(boolean affects);
+ // Paper end
+
/**
* Update the list of commands sent to the client.
* <br>
@@ -0,0 +1,62 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Isaac Moore <rmsy@me.com>
Date: Mon, 29 Feb 2016 18:02:25 -0600
Subject: [PATCH] Add PlayerLocaleChangeEvent
diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerLocaleChangeEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerLocaleChangeEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..29dd763a99ce7c6ecb176b9fb346a400369d48a0
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerLocaleChangeEvent.java
@@ -0,0 +1,50 @@
+package com.destroystokyo.paper.event.player;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerEvent;
+
+/**
+ * Called when the locale of the player is changed.
+ *
+ * @deprecated Replaced by {@link org.bukkit.event.player.PlayerLocaleChangeEvent} upstream
+ */
+@Deprecated
+public class PlayerLocaleChangeEvent extends PlayerEvent {
+ private static final HandlerList handlers = new HandlerList();
+ private final String oldLocale;
+ private final String newLocale;
+
+ public PlayerLocaleChangeEvent(final Player player, final String oldLocale, final String newLocale) {
+ super(player);
+ this.oldLocale = oldLocale;
+ this.newLocale = newLocale;
+ }
+
+ /**
+ * Gets the locale the player switched from.
+ *
+ * @return player's old locale
+ */
+ public String getOldLocale() {
+ return oldLocale;
+ }
+
+ /**
+ * Gets the locale the player is changed to.
+ *
+ * @return player's new locale
+ */
+ public String getNewLocale() {
+ return newLocale;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
-49
View File
@@ -1,49 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Mon, 29 Feb 2016 17:24:57 -0600
Subject: [PATCH] Add getTPS method
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index 7717f5346aa784df4a0f6e43a5a74a66f279bc8a..fba3e617a8847a3e9282e6a1a389132b2ad5511c 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -1964,6 +1964,17 @@ public final class Bukkit {
return server.getEntity(uuid);
}
+ // Paper start
+ /**
+ * Gets the current server TPS
+ * @return current server TPS (1m, 5m, 15m in Paper-Server)
+ */
+ @NotNull
+ public static double[] getTPS() {
+ return server.getTPS();
+ }
+ // Paper end
+
/**
* Get the advancement specified by this key.
*
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 0f990d33ab07249e4cf2d2269920b4cbeed27d39..c8cf66e64a3163c2bdf781f3ce52059546969b9b 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -1672,6 +1672,16 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
@Nullable
Entity getEntity(@NotNull UUID uuid);
+ // Paper start
+ /**
+ * Gets the current server TPS
+ *
+ * @return current server TPS (1m, 5m, 15m in Paper-Server)
+ */
+ @NotNull
+ public double[] getTPS();
+ // Paper end
+
/**
* Get the advancement specified by this key.
*
@@ -0,0 +1,133 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Byteflux <byte@byteflux.net>
Date: Mon, 29 Feb 2016 18:05:37 -0600
Subject: [PATCH] Add view distance API
Add per player no-tick, tick, and send view distances.
Also add send/no-tick view distance to World.
diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
index c58b1885662c6a234ffee75995051c9750c1a512..7b1fb280a86ab44756fc233b085f878d2a27ad66 100644
--- a/src/main/java/org/bukkit/World.java
+++ b/src/main/java/org/bukkit/World.java
@@ -2450,6 +2450,52 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
int getSimulationDistance();
// Spigot end
+ // Paper start - view distance api
+ /**
+ * Sets the view distance for this world.
+ * @param viewDistance view distance in [2, 32]
+ */
+ void setViewDistance(int viewDistance);
+
+ /**
+ * Returns the no-tick view distance for this world.
+ * <p>
+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
+ * be set to tick.
+ * </p>
+ * @return The no-tick view distance for this world.
+ */
+ int getNoTickViewDistance();
+
+ /**
+ * Sets the no-tick view distance for this world.
+ * <p>
+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
+ * be set to tick.
+ * </p>
+ * @param viewDistance view distance in [2, 32]
+ */
+ void setNoTickViewDistance(int viewDistance);
+
+ /**
+ * Gets the sending view distance for this world.
+ * <p>
+ * Sending view distance is the view distance where chunks will load in for players in this world.
+ * </p>
+ * @return The sending view distance for this world.
+ */
+ public int getSendViewDistance();
+
+ /**
+ * Sets the sending view distance for this world.
+ * <p>
+ * Sending view distance is the view distance where chunks will load in for players in this world.
+ * </p>
+ * @param viewDistance view distance in [2, 32] or -1
+ */
+ public void setSendViewDistance(int viewDistance);
+ // Paper end - view distance api
+
// Spigot start
public class Spigot {
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index a6f6dea965eee2de2183f189580d1b7141a9308f..bf60cc1beed41f6dc30b0df0bae9b937ddfa5553 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -1760,6 +1760,62 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* @param affects Whether the player can affect mob spawning
*/
public void setAffectsSpawning(boolean affects);
+
+ /**
+ * Gets the view distance for this player
+ *
+ * @return the player's view distance
+ * @see org.bukkit.World#getViewDistance()
+ * @see org.bukkit.World#getNoTickViewDistance()
+ */
+ public int getViewDistance();
+
+ /**
+ * Sets the view distance for this player
+ *
+ * @param viewDistance the player's view distance
+ * @see org.bukkit.World#setViewDistance(int)
+ * @see org.bukkit.World#setNoTickViewDistance(int)
+ */
+ public void setViewDistance(int viewDistance);
+
+ /**
+ * Gets the no-ticking view distance for this player.
+ * <p>
+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
+ * be set to tick.
+ * </p>
+ * @return The no-tick view distance for this player.
+ */
+ public int getNoTickViewDistance();
+
+ /**
+ * Sets the no-ticking view distance for this player.
+ * <p>
+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
+ * be set to tick.
+ * </p>
+ * @param viewDistance view distance in [2, 32] or -1
+ */
+ public void setNoTickViewDistance(int viewDistance);
+
+ /**
+ * Gets the sending view distance for this player.
+ * <p>
+ * Sending view distance is the view distance where chunks will load in for players.
+ * </p>
+ * @return The sending view distance for this player.
+ */
+ public int getSendViewDistance();
+
+ /**
+ * Sets the sending view distance for this player.
+ * <p>
+ * Sending view distance is the view distance where chunks will load in for players.
+ * </p>
+ * @param viewDistance view distance in [2, 32] or -1
+ */
+ public void setSendViewDistance(int viewDistance);
// Paper end
/**
-200
View File
@@ -1,200 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach@zachbr.io>
Date: Mon, 27 May 2019 01:10:06 -0500
Subject: [PATCH] Version Command 2.0
diff --git a/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..a736d7bcdc5861a01b66ba36158db1c716339346
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
@@ -0,0 +1,45 @@
+package com.destroystokyo.paper.util;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.Bukkit;
+import org.jetbrains.annotations.NotNull;
+
+public interface VersionFetcher {
+ /**
+ * Amount of time to cache results for in milliseconds
+ * <p>
+ * Negative values will never cache.
+ *
+ * @return cache time
+ */
+ long getCacheTime();
+
+ /**
+ * Gets the version message to cache and show to command senders.
+ *
+ * <p>NOTE: This is run in a new thread separate from that of the command processing thread</p>
+ *
+ * @param serverVersion the current version of the server (will match {@link Bukkit#getVersion()})
+ * @return the message to show when requesting a version
+ */
+ @NotNull
+ Component getVersionMessage(@NotNull String serverVersion);
+
+ class DummyVersionFetcher implements VersionFetcher {
+
+ @Override
+ public long getCacheTime() {
+ return -1;
+ }
+
+ @NotNull
+ @Override
+ public Component getVersionMessage(@NotNull String serverVersion) {
+ Bukkit.getLogger().warning("Version provider has not been set, cannot check for updates!");
+ Bukkit.getLogger().info("Override the default implementation of org.bukkit.UnsafeValues#getVersionFetcher()");
+ new Throwable().printStackTrace();
+ return Component.text("Unable to check for updates. No version provider set.", NamedTextColor.RED);
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
index f68ef89f37057cf677a767026ab395f7a839a2f9..a6aa33b9574d0278e10927007a62290e1d102e73 100644
--- a/src/main/java/org/bukkit/UnsafeValues.java
+++ b/src/main/java/org/bukkit/UnsafeValues.java
@@ -119,5 +119,12 @@ public interface UnsafeValues {
* @return name
*/
String getTimingsServerName();
+
+ /**
+ * Called once by the version command on first use, then cached.
+ */
+ default com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
+ return new com.destroystokyo.paper.util.VersionFetcher.DummyVersionFetcher();
+ }
// Paper end
}
diff --git a/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/src/main/java/org/bukkit/command/defaults/VersionCommand.java
index 04b4fb6859df0221f8f9f92c5a7ac2dda1073355..b437cf212a63aa96a9492db8d01d5d37061aee23 100644
--- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java
+++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java
@@ -24,8 +24,25 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
+// Paper start - version command 2.0
+import com.destroystokyo.paper.util.VersionFetcher;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.format.TextDecoration;
+import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
+// Paper end - version command 2.0
public class VersionCommand extends BukkitCommand {
+ private VersionFetcher versionFetcher; // Paper - version command 2.0
+ private VersionFetcher getVersionFetcher() { // lazy load because unsafe isn't available at command registration
+ if (versionFetcher == null) {
+ versionFetcher = Bukkit.getUnsafe().getVersionFetcher();
+ }
+
+ return versionFetcher;
+ }
+
public VersionCommand(@NotNull String name) {
super(name);
@@ -40,7 +57,7 @@ public class VersionCommand extends BukkitCommand {
if (!testPermission(sender)) return true;
if (args.length == 0) {
- sender.sendMessage("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")");
+ //sender.sendMessage("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")"); // Paper - moved to setVersionMessage
sendVersion(sender);
} else {
StringBuilder name = new StringBuilder();
@@ -79,8 +96,17 @@ public class VersionCommand extends BukkitCommand {
private void describeToSender(@NotNull Plugin plugin, @NotNull CommandSender sender) {
PluginDescriptionFile desc = plugin.getDescription();
- sender.sendMessage(ChatColor.GREEN + desc.getName() + ChatColor.WHITE + " version " + ChatColor.GREEN + desc.getVersion());
-
+ // Paper start - version command 2.0
+ sender.sendMessage(
+ Component.text()
+ .append(Component.text(desc.getName(), NamedTextColor.GREEN))
+ .append(Component.text(" version "))
+ .append(Component.text(desc.getVersion(), NamedTextColor.GREEN)
+ .hoverEvent(Component.text("Click to copy to clipboard", NamedTextColor.WHITE))
+ .clickEvent(ClickEvent.copyToClipboard(desc.getVersion()))
+ )
+ );
+ // Paper end - version command 2.0
if (desc.getDescription() != null) {
sender.sendMessage(desc.getDescription());
}
@@ -146,14 +172,14 @@ public class VersionCommand extends BukkitCommand {
private final ReentrantLock versionLock = new ReentrantLock();
private boolean hasVersion = false;
- private String versionMessage = null;
+ private Component versionMessage = null; // Paper
private final Set<CommandSender> versionWaiters = new HashSet<CommandSender>();
private boolean versionTaskStarted = false;
private long lastCheck = 0;
private void sendVersion(@NotNull CommandSender sender) {
if (hasVersion) {
- if (System.currentTimeMillis() - lastCheck > 21600000) {
+ if (System.currentTimeMillis() - lastCheck > getVersionFetcher().getCacheTime()) { // Paper - use version supplier
lastCheck = System.currentTimeMillis();
hasVersion = false;
} else {
@@ -168,7 +194,7 @@ public class VersionCommand extends BukkitCommand {
return;
}
versionWaiters.add(sender);
- sender.sendMessage("Checking version, please wait...");
+ sender.sendMessage(Component.text("Checking version, please wait...", NamedTextColor.WHITE, TextDecoration.ITALIC)); // Paper
if (!versionTaskStarted) {
versionTaskStarted = true;
new Thread(new Runnable() {
@@ -186,6 +212,13 @@ public class VersionCommand extends BukkitCommand {
private void obtainVersion() {
String version = Bukkit.getVersion();
+ // Paper start
+ if (version.startsWith("null")) { // running from ide?
+ setVersionMessage(Component.text("Unknown version, custom build?", NamedTextColor.YELLOW));
+ return;
+ }
+ setVersionMessage(getVersionFetcher().getVersionMessage(version));
+ /*
if (version == null) version = "Custom";
String[] parts = version.substring(0, version.indexOf(' ')).split("-");
if (parts.length == 4) {
@@ -215,11 +248,24 @@ public class VersionCommand extends BukkitCommand {
} else {
setVersionMessage("Unknown version, custom build?");
}
+ */
+ // Paper end
}
- private void setVersionMessage(@NotNull String msg) {
+ // Paper start
+ private void setVersionMessage(final @NotNull Component msg) {
lastCheck = System.currentTimeMillis();
- versionMessage = msg;
+ final Component message = Component.textOfChildren(
+ Component.text("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")", NamedTextColor.WHITE),
+ Component.newline(),
+ msg
+ );
+ this.versionMessage = Component.text()
+ .append(message)
+ .hoverEvent(Component.text("Click to copy to clipboard", NamedTextColor.WHITE))
+ .clickEvent(ClickEvent.copyToClipboard(PlainTextComponentSerializer.plainText().serialize(message)))
+ .build();
+ // Paper end
versionLock.lock();
try {
hasVersion = true;
-66
View File
@@ -1,66 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Byteflux <byte@byteflux.net>
Date: Mon, 29 Feb 2016 17:50:31 -0600
Subject: [PATCH] Entity Origin API
diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
index 598abaa82c634178043a29f6caa6ac5221659d06..70a32f89c16439c575d8928332cfed64d1d6ab7e 100644
--- a/src/main/java/org/bukkit/entity/Entity.java
+++ b/src/main/java/org/bukkit/entity/Entity.java
@@ -725,5 +725,15 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.customName())));
}
+
+ /**
+ * Gets the location where this entity originates from.
+ * <p>
+ * This value can be null if the entity hasn't yet been added to the world.
+ *
+ * @return Location where entity originates or null if not yet added
+ */
+ @Nullable
+ Location getOrigin();
// Paper end
}
diff --git a/src/main/java/org/bukkit/entity/FallingBlock.java b/src/main/java/org/bukkit/entity/FallingBlock.java
index d96fd494b523491fcf1ac54d164351fd487f65d6..ae1ce2ee2deb82f3f4144ec54b3ba119b437c5e9 100644
--- a/src/main/java/org/bukkit/entity/FallingBlock.java
+++ b/src/main/java/org/bukkit/entity/FallingBlock.java
@@ -126,4 +126,15 @@ public interface FallingBlock extends Entity {
* @param damage the max damage to set. Must be >= 0
*/
void setMaxDamage(int damage);
+
+ /**
+ * Gets the source block location of the FallingBlock
+ *
+ * @return the source block location the FallingBlock was spawned from
+ * @deprecated replaced by {@link Entity#getOrigin()}
+ */
+ @Deprecated
+ default org.bukkit.Location getSourceLoc() {
+ return this.getOrigin();
+ }
}
diff --git a/src/main/java/org/bukkit/entity/TNTPrimed.java b/src/main/java/org/bukkit/entity/TNTPrimed.java
index a23cfdf66877f0a61eae700de084c76e6ee7b431..0813bd913c8fdb2001963ce3e82c07c2af105418 100644
--- a/src/main/java/org/bukkit/entity/TNTPrimed.java
+++ b/src/main/java/org/bukkit/entity/TNTPrimed.java
@@ -53,4 +53,15 @@ public interface TNTPrimed extends Explosive {
* @param source the source of this primed TNT
*/
public void setSource(@Nullable Entity source);
+
+ /**
+ * Gets the source block location of the TNTPrimed
+ *
+ * @return the source block location the TNTPrimed was spawned from
+ * @deprecated replaced by {@link Entity#getOrigin()}
+ */
+ @Deprecated
+ default org.bukkit.Location getSourceLoc() {
+ return this.getOrigin();
+ }
}
@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Steve Anton <anxuiz.nx@gmail.com>
Date: Mon, 29 Feb 2016 18:13:58 -0600
Subject: [PATCH] Add PlayerInitialSpawnEvent
For modifying a player's initial spawn location as they join the server
diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerInitialSpawnEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerInitialSpawnEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..626e97bbcaa19d55475a0fc8770412d437af2733
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerInitialSpawnEvent.java
@@ -0,0 +1,17 @@
+package com.destroystokyo.paper.event.player;
+
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.spigotmc.event.player.PlayerSpawnLocationEvent;
+
+/**
+ * @deprecated Use {@link PlayerSpawnLocationEvent}, Duplicate API
+ */
+@Deprecated
+public class PlayerInitialSpawnEvent extends PlayerSpawnLocationEvent {
+
+ public PlayerInitialSpawnEvent(@NotNull Player who, @NotNull Location spawnLocation) {
+ super(who, spawnLocation);
+ }
+}
@@ -1,63 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Isaac Moore <rmsy@me.com>
Date: Mon, 29 Feb 2016 18:02:25 -0600
Subject: [PATCH] Add PlayerLocaleChangeEvent
diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerLocaleChangeEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerLocaleChangeEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb7baa91fd4ba763fd8ce216bc9043c5e2c4b7b7
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerLocaleChangeEvent.java
@@ -0,0 +1,51 @@
+package com.destroystokyo.paper.event.player;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerEvent;
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * Called when the locale of the player is changed.
+ *
+ * @deprecated Replaced by {@link org.bukkit.event.player.PlayerLocaleChangeEvent} upstream
+ */
+@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21")
+public class PlayerLocaleChangeEvent extends PlayerEvent {
+ private static final HandlerList handlers = new HandlerList();
+ private final String oldLocale;
+ private final String newLocale;
+
+ public PlayerLocaleChangeEvent(final Player player, final String oldLocale, final String newLocale) {
+ super(player);
+ this.oldLocale = oldLocale;
+ this.newLocale = newLocale;
+ }
+
+ /**
+ * Gets the locale the player switched from.
+ *
+ * @return player's old locale
+ */
+ public String getOldLocale() {
+ return oldLocale;
+ }
+
+ /**
+ * Gets the locale the player is changed to.
+ *
+ * @return player's new locale
+ */
+ public String getNewLocale() {
+ return newLocale;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
@@ -1,159 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Byteflux <byte@byteflux.net>
Date: Mon, 29 Feb 2016 18:05:37 -0600
Subject: [PATCH] Add view distance API
Add per player no-tick, tick, and send view distances.
Also add send/no-tick view distance to World.
diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
index 5357291ff0f2f20bd87ab9f6e57f6a4f6ff65226..887aa6217583d224d66f6d238ac269c23725d459 100644
--- a/src/main/java/org/bukkit/World.java
+++ b/src/main/java/org/bukkit/World.java
@@ -2662,6 +2662,62 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
int getSimulationDistance();
// Spigot end
+ // Paper start - view distance api
+ /**
+ * Sets the view distance for this world.
+ * @param viewDistance view distance in [2, 32]
+ */
+ void setViewDistance(int viewDistance);
+
+ /**
+ * Sets the simulation distance for this world.
+ * @param simulationDistance simulation distance in [2, 32]
+ */
+ void setSimulationDistance(int simulationDistance);
+
+ /**
+ * Returns the no-tick view distance for this world.
+ * <p>
+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
+ * be set to tick.
+ * </p>
+ * @return The no-tick view distance for this world.
+ * @deprecated Use {@link #getViewDistance()}
+ */
+ @Deprecated
+ int getNoTickViewDistance();
+
+ /**
+ * Sets the no-tick view distance for this world.
+ * <p>
+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
+ * be set to tick.
+ * </p>
+ * @param viewDistance view distance in [2, 32]
+ * @deprecated Use {@link #setViewDistance(int)}
+ */
+ @Deprecated
+ void setNoTickViewDistance(int viewDistance);
+
+ /**
+ * Gets the sending view distance for this world.
+ * <p>
+ * Sending view distance is the view distance where chunks will load in for players in this world.
+ * </p>
+ * @return The sending view distance for this world.
+ */
+ int getSendViewDistance();
+
+ /**
+ * Sets the sending view distance for this world.
+ * <p>
+ * Sending view distance is the view distance where chunks will load in for players in this world.
+ * </p>
+ * @param viewDistance view distance in [2, 32] or -1
+ */
+ void setSendViewDistance(int viewDistance);
+ // Paper end - view distance api
+
// Spigot start
public class Spigot {
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 6b1a198f930a5a995e0cb6c279b455e1639a5039..f8e45f70fe23cf73c8677b5d24cefcd3aae5e24e 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -2210,6 +2210,78 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* @param affects Whether the player can affect mob spawning
*/
public void setAffectsSpawning(boolean affects);
+
+ /**
+ * Gets the view distance for this player
+ *
+ * @return the player's view distance
+ * @see org.bukkit.World#getViewDistance()
+ */
+ public int getViewDistance();
+
+ /**
+ * Sets the view distance for this player
+ *
+ * @param viewDistance the player's view distance
+ * @see org.bukkit.World#setViewDistance(int)
+ */
+ public void setViewDistance(int viewDistance);
+
+ /**
+ * Gets the simulation distance for this player
+ *
+ * @return the player's simulation distance
+ */
+ public int getSimulationDistance();
+
+ /**
+ * Sets the simulation distance for this player
+ *
+ * @param simulationDistance the player's new simulation distance
+ */
+ public void setSimulationDistance(int simulationDistance);
+
+ /**
+ * Gets the no-ticking view distance for this player.
+ * <p>
+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
+ * be set to tick.
+ * </p>
+ * @return The no-tick view distance for this player.
+ * @deprecated Use {@link #getViewDistance()}
+ */
+ @Deprecated
+ public int getNoTickViewDistance();
+
+ /**
+ * Sets the no-ticking view distance for this player.
+ * <p>
+ * No-tick view distance is the view distance where chunks will load, however the chunks and their entities will not
+ * be set to tick.
+ * </p>
+ * @param viewDistance view distance in [2, 32] or -1
+ * @deprecated Use {@link #setViewDistance(int)}
+ */
+ @Deprecated
+ public void setNoTickViewDistance(int viewDistance);
+
+ /**
+ * Gets the sending view distance for this player.
+ * <p>
+ * Sending view distance is the view distance where chunks will load in for players.
+ * </p>
+ * @return The sending view distance for this player.
+ */
+ public int getSendViewDistance();
+
+ /**
+ * Sets the sending view distance for this player.
+ * <p>
+ * Sending view distance is the view distance where chunks will load in for players.
+ * </p>
+ * @param viewDistance view distance in [2, 32] or -1
+ */
+ public void setSendViewDistance(int viewDistance);
// Paper end
/**
@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Mon, 29 Feb 2016 19:45:21 -0600
Subject: [PATCH] Automatically disable plugins that fail to load
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
index 9700ed558e65260264279b92dc661f39a0d0209c..451fce7223834169751679ce6dc22de3df24eae9 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
@@ -335,6 +335,10 @@ public final class JavaPluginLoader implements PluginLoader {
jPlugin.setEnabled(true);
} catch (Throwable ex) {
server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ // Paper start - Disable plugins that fail to load
+ this.server.getPluginManager().disablePlugin(jPlugin);
+ return;
+ // Paper end
}
// Perhaps abort here, rather than continue going, but as it stands,
@@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kashike <kashike@vq.lc>
Date: Mon, 29 Feb 2016 19:48:59 -0600
Subject: [PATCH] Expose server CommandMap
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index bbee4e40917cca4e692c3d60c1c513b4c847160f..259c15ada05115b9fc86f1de06649d5cec2ef137 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -1912,6 +1912,19 @@ public final class Bukkit {
return server.getUnsafe();
}
+
+ // Paper start
+ /**
+ * Gets the active {@link org.bukkit.command.CommandMap}
+ *
+ * @return the active command map
+ */
+ @NotNull
+ public static org.bukkit.command.CommandMap getCommandMap() {
+ return server.getCommandMap();
+ }
+ // Paper end
+
@NotNull
public static Server.Spigot spigot() {
return server.spigot();
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 835041d2ab6545e52d963d96d558f9a7e52d279a..66211ef974b9e091983bfda108f6e420c01a9d78 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -1471,6 +1471,15 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
public double[] getTPS();
// Paper end
+ // Paper start
+ /**
+ * Gets the active {@link org.bukkit.command.CommandMap}
+ *
+ * @return the active command map
+ */
+ @NotNull
+ org.bukkit.command.CommandMap getCommandMap();
+
/**
* Get the advancement specified by this key.
*
@@ -1,31 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Steve Anton <anxuiz.nx@gmail.com>
Date: Mon, 29 Feb 2016 18:13:58 -0600
Subject: [PATCH] Add PlayerInitialSpawnEvent
For modifying a player's initial spawn location as they join the server
diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerInitialSpawnEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerInitialSpawnEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..febd384b43560d7d55c96bda5b0701a27fc9e18e
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerInitialSpawnEvent.java
@@ -0,0 +1,18 @@
+package com.destroystokyo.paper.event.player;
+
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.spigotmc.event.player.PlayerSpawnLocationEvent;
+
+/**
+ * @deprecated Use {@link PlayerSpawnLocationEvent}, Duplicate API
+ */
+@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21")
+public class PlayerInitialSpawnEvent extends PlayerSpawnLocationEvent {
+
+ public PlayerInitialSpawnEvent(@NotNull Player who, @NotNull Location spawnLocation) {
+ super(who, spawnLocation);
+ }
+}
@@ -0,0 +1,124 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach.brown@destroystokyo.com>
Date: Mon, 29 Feb 2016 19:54:32 -0600
Subject: [PATCH] Graduate bungeecord chat API from spigot subclasses
Change Javadoc to be accurate
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index 259c15ada05115b9fc86f1de06649d5cec2ef137..5fc71e24f1562ff0d448b964f29fd109d4d9bb4d 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -376,6 +376,30 @@ public final class Bukkit {
return server.broadcastMessage(message);
}
+ // Paper start
+ /**
+ * Sends the component to all online players.
+ *
+ * @param component the component to send
+ * @deprecated use {@code sendMessage} methods on {@link #getServer()} that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Deprecated
+ public static void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ server.broadcast(component);
+ }
+
+ /**
+ * Sends an array of components as a single message to all online players.
+ *
+ * @param components the components to send
+ * @deprecated use {@code sendMessage} methods on {@link #getServer()} that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Deprecated
+ public static void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ server.broadcast(components);
+ }
+ // Paper end
+
/**
* Gets the name of the update folder. The update folder is used to safely
* update plugins at the right moment on a plugin load.
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 66211ef974b9e091983bfda108f6e420c01a9d78..8fb6545406ab6029d82c903856bda6c6d6fa0636 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -309,6 +309,30 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
@Deprecated // Paper
public int broadcastMessage(@NotNull String message);
+ // Paper start
+ /**
+ * Sends the component to all online players.
+ *
+ * @param component the component to send
+ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Deprecated
+ public default void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ spigot().broadcast(component);
+ }
+
+ /**
+ * Sends an array of components as a single message to all online players.
+ *
+ * @param components the components to send
+ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Deprecated
+ public default void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ spigot().broadcast(components);
+ }
+ // Paper end
+
/**
* Gets the name of the update folder. The update folder is used to safely
* update plugins at the right moment on a plugin load.
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 152f848d2f93a52999f093bd83b4732af9aab8d8..771682aa1c5e1bfb9670cc98ce99439014d0d31c 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -708,6 +708,42 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
*/
public void sendMap(@NotNull MapView map);
+ // Paper start
+ /**
+ * Sends the component to the player
+ *
+ * @param component the components to send
+ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Override
+ @Deprecated
+ public default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ spigot().sendMessage(component);
+ }
+
+ /**
+ * Sends an array of components as a single message to the player
+ *
+ * @param components the components to send
+ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Override
+ @Deprecated
+ public default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ spigot().sendMessage(components);
+ }
+
+ /**
+ * Sends an array of components as a single message to the specified screen position of this player
+ *
+ * @param position the screen position
+ * @param components the components to send
+ */
+ public default void sendMessage(net.md_5.bungee.api.ChatMessageType position, net.md_5.bungee.api.chat.BaseComponent... components) {
+ spigot().sendMessage(position, components);
+ }
+ // Paper end
+
/**
* Forces an update of the player's entire inventory.
*
@@ -0,0 +1,604 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach.brown@destroystokyo.com>
Date: Mon, 29 Feb 2016 20:24:35 -0600
Subject: [PATCH] Add exception reporting event
diff --git a/src/main/java/com/destroystokyo/paper/event/server/ServerExceptionEvent.java b/src/main/java/com/destroystokyo/paper/event/server/ServerExceptionEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f573299a9a817a98372817a1de8bf641aaca956
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/server/ServerExceptionEvent.java
@@ -0,0 +1,43 @@
+package com.destroystokyo.paper.event.server;
+
+import com.google.common.base.Preconditions;
+import org.apache.commons.lang.Validate;
+import org.bukkit.Bukkit;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import com.destroystokyo.paper.exception.ServerException;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Called whenever an exception is thrown in a recoverable section of the server.
+ */
+public class ServerExceptionEvent extends Event {
+ private static final HandlerList handlers = new HandlerList();
+ @NotNull private ServerException exception;
+
+ public ServerExceptionEvent(@NotNull ServerException exception) {
+ super(!Bukkit.isPrimaryThread());
+ this.exception = Preconditions.checkNotNull(exception, "exception");
+ }
+
+ /**
+ * Gets the wrapped exception that was thrown.
+ *
+ * @return Exception thrown
+ */
+ @NotNull
+ public ServerException getException() {
+ return exception;
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerCommandException.java b/src/main/java/com/destroystokyo/paper/exception/ServerCommandException.java
new file mode 100644
index 0000000000000000000000000000000000000000..6fb39af0479a818f7f1465bcdfe505ab4ff7da1a
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerCommandException.java
@@ -0,0 +1,64 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Thrown when a command throws an exception
+ */
+public class ServerCommandException extends ServerException {
+
+ private final Command command;
+ private final CommandSender commandSender;
+ private final String[] arguments;
+
+ public ServerCommandException(String message, Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
+ super(message, cause);
+ this.commandSender = checkNotNull(commandSender, "commandSender");
+ this.arguments = checkNotNull(arguments, "arguments");
+ this.command = checkNotNull(command, "command");
+ }
+
+ public ServerCommandException(Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
+ super(cause);
+ this.commandSender = checkNotNull(commandSender, "commandSender");
+ this.arguments = checkNotNull(arguments, "arguments");
+ this.command = checkNotNull(command, "command");
+ }
+
+ protected ServerCommandException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Command command, CommandSender commandSender, String[] arguments) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ this.commandSender = checkNotNull(commandSender, "commandSender");
+ this.arguments = checkNotNull(arguments, "arguments");
+ this.command = checkNotNull(command, "command");
+ }
+
+ /**
+ * Gets the command which threw the exception
+ *
+ * @return exception throwing command
+ */
+ public Command getCommand() {
+ return command;
+ }
+
+ /**
+ * Gets the command sender which executed the command request
+ *
+ * @return command sender of exception thrown command request
+ */
+ public CommandSender getCommandSender() {
+ return commandSender;
+ }
+
+ /**
+ * Gets the arguments which threw the exception for the command
+ *
+ * @return arguments of exception thrown command request
+ */
+ public String[] getArguments() {
+ return arguments;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerEventException.java b/src/main/java/com/destroystokyo/paper/exception/ServerEventException.java
new file mode 100644
index 0000000000000000000000000000000000000000..410b24139535cd5d8439ad581c43c61b5757fbf6
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerEventException.java
@@ -0,0 +1,52 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.event.Event;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.Plugin;
+
+import static com.google.common.base.Preconditions.*;
+
+/**
+ * Exception thrown when a server event listener throws an exception
+ */
+public class ServerEventException extends ServerPluginException {
+
+ private final Listener listener;
+ private final Event event;
+
+ public ServerEventException(String message, Throwable cause, Plugin responsiblePlugin, Listener listener, Event event) {
+ super(message, cause, responsiblePlugin);
+ this.listener = checkNotNull(listener, "listener");
+ this.event = checkNotNull(event, "event");
+ }
+
+ public ServerEventException(Throwable cause, Plugin responsiblePlugin, Listener listener, Event event) {
+ super(cause, responsiblePlugin);
+ this.listener = checkNotNull(listener, "listener");
+ this.event = checkNotNull(event, "event");
+ }
+
+ protected ServerEventException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin, Listener listener, Event event) {
+ super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
+ this.listener = checkNotNull(listener, "listener");
+ this.event = checkNotNull(event, "event");
+ }
+
+ /**
+ * Gets the listener which threw the exception
+ *
+ * @return event listener
+ */
+ public Listener getListener() {
+ return listener;
+ }
+
+ /**
+ * Gets the event which caused the exception
+ *
+ * @return event
+ */
+ public Event getEvent() {
+ return event;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerException.java b/src/main/java/com/destroystokyo/paper/exception/ServerException.java
new file mode 100644
index 0000000000000000000000000000000000000000..c06ea3942447d4824b83ff839cb449fb818dede1
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerException.java
@@ -0,0 +1,23 @@
+package com.destroystokyo.paper.exception;
+
+/**
+ * Wrapper exception for all exceptions that are thrown by the server.
+ */
+public class ServerException extends Exception {
+
+ public ServerException(String message) {
+ super(message);
+ }
+
+ public ServerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ServerException(Throwable cause) {
+ super(cause);
+ }
+
+ protected ServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerInternalException.java b/src/main/java/com/destroystokyo/paper/exception/ServerInternalException.java
new file mode 100644
index 0000000000000000000000000000000000000000..e762ed0dbad51625e65fef2e1898679108459a36
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerInternalException.java
@@ -0,0 +1,35 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.Bukkit;
+import com.destroystokyo.paper.event.server.ServerExceptionEvent;
+
+/**
+ * Thrown when the internal server throws a recoverable exception.
+ */
+public class ServerInternalException extends ServerException {
+
+ public ServerInternalException(String message) {
+ super(message);
+ }
+
+ public ServerInternalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ServerInternalException(Throwable cause) {
+ super(cause);
+ }
+
+ protected ServerInternalException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public static void reportInternalException(Throwable cause) {
+ try {
+ Bukkit.getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(cause)));
+ ;
+ } catch (Throwable t) {
+ t.printStackTrace(); // Don't want to rethrow!
+ }
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerPluginEnableDisableException.java b/src/main/java/com/destroystokyo/paper/exception/ServerPluginEnableDisableException.java
new file mode 100644
index 0000000000000000000000000000000000000000..f016ba3b1b62e554a9bacbb9635f2dbe441b3c4e
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerPluginEnableDisableException.java
@@ -0,0 +1,20 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.plugin.Plugin;
+
+/**
+ * Thrown whenever there is an exception with any enabling or disabling of plugins.
+ */
+public class ServerPluginEnableDisableException extends ServerPluginException {
+ public ServerPluginEnableDisableException(String message, Throwable cause, Plugin responsiblePlugin) {
+ super(message, cause, responsiblePlugin);
+ }
+
+ public ServerPluginEnableDisableException(Throwable cause, Plugin responsiblePlugin) {
+ super(cause, responsiblePlugin);
+ }
+
+ protected ServerPluginEnableDisableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin) {
+ super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerPluginException.java b/src/main/java/com/destroystokyo/paper/exception/ServerPluginException.java
new file mode 100644
index 0000000000000000000000000000000000000000..6defac287d0214fdf99418d979144050cc1e53bc
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerPluginException.java
@@ -0,0 +1,38 @@
+package com.destroystokyo.paper.exception;
+
+import com.google.common.base.Preconditions;
+import org.apache.commons.lang.Validate;
+import org.bukkit.plugin.Plugin;
+
+import static com.google.common.base.Preconditions.*;
+
+/**
+ * Wrapper exception for all cases to which a plugin can be immediately blamed for
+ */
+public class ServerPluginException extends ServerException {
+ public ServerPluginException(String message, Throwable cause, Plugin responsiblePlugin) {
+ super(message, cause);
+ this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
+ }
+
+ public ServerPluginException(Throwable cause, Plugin responsiblePlugin) {
+ super(cause);
+ this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
+ }
+
+ protected ServerPluginException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
+ }
+
+ private final Plugin responsiblePlugin;
+
+ /**
+ * Gets the plugin which is directly responsible for the exception being thrown
+ *
+ * @return plugin which is responsible for the exception throw
+ */
+ public Plugin getResponsiblePlugin() {
+ return responsiblePlugin;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java b/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java
new file mode 100644
index 0000000000000000000000000000000000000000..89e132525cfae0ce979e37b3e2793df781e47227
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java
@@ -0,0 +1,64 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
+
+import static com.google.common.base.Preconditions.*;
+
+/**
+ * Thrown when an incoming plugin message channel throws an exception
+ */
+public class ServerPluginMessageException extends ServerPluginException {
+
+ private final Player player;
+ private final String channel;
+ private final byte[] data;
+
+ public ServerPluginMessageException(String message, Throwable cause, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
+ super(message, cause, responsiblePlugin);
+ this.player = checkNotNull(player, "player");
+ this.channel = checkNotNull(channel, "channel");
+ this.data = checkNotNull(data, "data");
+ }
+
+ public ServerPluginMessageException(Throwable cause, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
+ super(cause, responsiblePlugin);
+ this.player = checkNotNull(player, "player");
+ this.channel = checkNotNull(channel, "channel");
+ this.data = checkNotNull(data, "data");
+ }
+
+ protected ServerPluginMessageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
+ super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
+ this.player = checkNotNull(player, "player");
+ this.channel = checkNotNull(channel, "channel");
+ this.data = checkNotNull(data, "data");
+ }
+
+ /**
+ * Gets the channel to which the error occurred from recieving data from
+ *
+ * @return exception channel
+ */
+ public String getChannel() {
+ return channel;
+ }
+
+ /**
+ * Gets the data to which the error occurred from
+ *
+ * @return exception data
+ */
+ public byte[] getData() {
+ return data;
+ }
+
+ /**
+ * Gets the player which the plugin message causing the exception originated from
+ *
+ * @return exception player
+ */
+ public Player getPlayer() {
+ return player;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerSchedulerException.java b/src/main/java/com/destroystokyo/paper/exception/ServerSchedulerException.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d0b2d4a9b3e5bdeec0e4ea7ab69858d86aa3715
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerSchedulerException.java
@@ -0,0 +1,37 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.scheduler.BukkitTask;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Thrown when a plugin's scheduler fails with an exception
+ */
+public class ServerSchedulerException extends ServerPluginException {
+
+ private final BukkitTask task;
+
+ public ServerSchedulerException(String message, Throwable cause, BukkitTask task) {
+ super(message, cause, task.getOwner());
+ this.task = checkNotNull(task, "task");
+ }
+
+ public ServerSchedulerException(Throwable cause, BukkitTask task) {
+ super(cause, task.getOwner());
+ this.task = checkNotNull(task, "task");
+ }
+
+ protected ServerSchedulerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, BukkitTask task) {
+ super(message, cause, enableSuppression, writableStackTrace, task.getOwner());
+ this.task = checkNotNull(task, "task");
+ }
+
+ /**
+ * Gets the task which threw the exception
+ *
+ * @return exception throwing task
+ */
+ public BukkitTask getTask() {
+ return task;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerTabCompleteException.java b/src/main/java/com/destroystokyo/paper/exception/ServerTabCompleteException.java
new file mode 100644
index 0000000000000000000000000000000000000000..5582999fe94c7a3dac655044ccc6d078cd9521a1
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerTabCompleteException.java
@@ -0,0 +1,22 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+/**
+ * Called when a tab-complete request throws an exception
+ */
+public class ServerTabCompleteException extends ServerCommandException {
+
+ public ServerTabCompleteException(String message, Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
+ super(message, cause, command, commandSender, arguments);
+ }
+
+ public ServerTabCompleteException(Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
+ super(cause, command, commandSender, arguments);
+ }
+
+ protected ServerTabCompleteException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Command command, CommandSender commandSender, String[] arguments) {
+ super(message, cause, enableSuppression, writableStackTrace, command, commandSender, arguments);
+ }
+}
diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java
index f020cb04eba27a2e70fc7cf799ebbfb434b9d974..adfc7aae2c0f49bbcdd358e83b04a0cf078a7d52 100644
--- a/src/main/java/org/bukkit/command/SimpleCommandMap.java
+++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java
@@ -8,6 +8,10 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+
+import com.destroystokyo.paper.event.server.ServerExceptionEvent;
+import com.destroystokyo.paper.exception.ServerCommandException;
+import com.destroystokyo.paper.exception.ServerTabCompleteException;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Server;
@@ -155,11 +159,14 @@ public class SimpleCommandMap implements CommandMap {
target.execute(sender, sentCommandLabel, Arrays.copyOfRange(args, 1, args.length));
} // target.timings.stopTiming(); // Spigot // Paper
} catch (CommandException ex) {
+ server.getPluginManager().callEvent(new ServerExceptionEvent(new ServerCommandException(ex, target, sender, args))); // Paper
//target.timings.stopTiming(); // Spigot // Paper
throw ex;
} catch (Throwable ex) {
//target.timings.stopTiming(); // Spigot // Paper
- throw new CommandException("Unhandled exception executing '" + commandLine + "' in " + target, ex);
+ String msg = "Unhandled exception executing '" + commandLine + "' in " + target;
+ server.getPluginManager().callEvent(new ServerExceptionEvent(new ServerCommandException(ex, target, sender, args))); // Paper
+ throw new CommandException(msg, ex);
}
// return true as command was handled
@@ -238,7 +245,9 @@ public class SimpleCommandMap implements CommandMap {
} catch (CommandException ex) {
throw ex;
} catch (Throwable ex) {
- throw new CommandException("Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target, ex);
+ String msg = "Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target;
+ server.getPluginManager().callEvent(new ServerExceptionEvent(new ServerTabCompleteException(msg, ex, target, sender, args))); // Paper
+ throw new CommandException(msg, ex);
}
}
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index 83d3ee09cc6f91b095f6b8519a431ef70f2b67ec..1fbf690403eefe017b146c141c6d47d9b8d9d4bb 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -23,6 +23,10 @@ import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
+import com.destroystokyo.paper.event.server.ServerExceptionEvent;
+import com.destroystokyo.paper.exception.ServerEventException;
+import com.destroystokyo.paper.exception.ServerPluginEnableDisableException;
import org.apache.commons.lang.Validate;
import org.bukkit.Server;
import org.bukkit.World;
@@ -488,7 +492,8 @@ public final class SimplePluginManager implements PluginManager {
try {
plugin.getPluginLoader().enablePlugin(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while enabling "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin);
}
HandlerList.bakeAll();
@@ -509,32 +514,37 @@ public final class SimplePluginManager implements PluginManager {
try {
plugin.getPluginLoader().disablePlugin(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while disabling "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getScheduler().cancelTasks(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while cancelling tasks for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while cancelling tasks for "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getServicesManager().unregisterAll(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering services for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while unregistering services for "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
HandlerList.unregisterAll(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering events for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while unregistering events for "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getMessenger().unregisterIncomingPluginChannel(plugin);
server.getMessenger().unregisterOutgoingPluginChannel(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering plugin channels for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while unregistering plugin channels for "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
@@ -547,6 +557,13 @@ public final class SimplePluginManager implements PluginManager {
}
}
+ // Paper start
+ private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
+ server.getLogger().log(Level.SEVERE, msg, ex);
+ callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin)));
+ }
+ // Paper end
+
@Override
public void clearPlugins() {
synchronized (this) {
@@ -610,7 +627,13 @@ public final class SimplePluginManager implements PluginManager {
));
}
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(), ex);
+ // Paper start - error reporting
+ String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName();
+ server.getLogger().log(Level.SEVERE, msg, ex);
+ if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop
+ callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
+ }
+ // Paper end
}
}
}
@@ -1,50 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kashike <kashike@vq.lc>
Date: Mon, 29 Feb 2016 19:48:59 -0600
Subject: [PATCH] Expose server CommandMap
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index fba3e617a8847a3e9282e6a1a389132b2ad5511c..acb34296923fb27cfc28c130cf0b5405cfe340f6 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -2165,6 +2165,19 @@ public final class Bukkit {
return server.getUnsafe();
}
+
+ // Paper start
+ /**
+ * Gets the active {@link org.bukkit.command.CommandMap}
+ *
+ * @return the active command map
+ */
+ @NotNull
+ public static org.bukkit.command.CommandMap getCommandMap() {
+ return server.getCommandMap();
+ }
+ // Paper end
+
@NotNull
public static Server.Spigot spigot() {
return server.spigot();
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index c8cf66e64a3163c2bdf781f3ce52059546969b9b..5b3c34b01038849b1463870ccf657b11db82cd09 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -1682,6 +1682,15 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
public double[] getTPS();
// Paper end
+ // Paper start
+ /**
+ * Gets the active {@link org.bukkit.command.CommandMap}
+ *
+ * @return the active command map
+ */
+ @NotNull
+ org.bukkit.command.CommandMap getCommandMap();
+
/**
* Get the advancement specified by this key.
*
@@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kashike <kashike@vq.lc>
Date: Tue, 8 Mar 2016 13:05:59 -0800
Subject: [PATCH] Add BaseComponent sendMessage methods to CommandSender
diff --git a/src/main/java/org/bukkit/command/CommandSender.java b/src/main/java/org/bukkit/command/CommandSender.java
index f98d46fa45bb7c842d791b09f082a0968ae537fb..e330a0d2be7a73ff84ec18499de6aecbceb5166c 100644
--- a/src/main/java/org/bukkit/command/CommandSender.java
+++ b/src/main/java/org/bukkit/command/CommandSender.java
@@ -1,6 +1,9 @@
package org.bukkit.command;
import java.util.UUID;
+import net.kyori.adventure.audience.MessageType;
+import net.kyori.adventure.identity.Identity;
+import net.kyori.adventure.text.Component;
import org.bukkit.Server;
import org.bukkit.permissions.Permissible;
import org.jetbrains.annotations.NotNull;
@@ -124,5 +127,33 @@ public interface CommandSender extends net.kyori.adventure.audience.Audience, Pe
default void sendMessage(final @NotNull net.kyori.adventure.identity.Identity identity, final @NotNull net.kyori.adventure.text.Component message, final @NotNull net.kyori.adventure.audience.MessageType type) {
this.sendMessage(org.bukkit.Bukkit.getUnsafe().legacyComponentSerializer().serialize(message));
}
+
+ /**
+ * Sends the component to the sender
+ *
+ * <p>If this sender does not support sending full components then
+ * the component will be sent as legacy text.</p>
+ *
+ * @param component the component to send
+ * @deprecated use {@link #sendMessage(Identity, Component, MessageType)} instead
+ */
+ @Deprecated
+ default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ this.sendMessage(component.toLegacyText());
+ }
+
+ /**
+ * Sends an array of components as a single message to the sender
+ *
+ * <p>If this sender does not support sending full components then
+ * the components will be sent as legacy text.</p>
+ *
+ * @param components the components to send
+ * @deprecated use {@link #sendMessage(Identity, Component, MessageType)} instead
+ */
+ @Deprecated
+ default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ this.sendMessage(new net.md_5.bungee.api.chat.TextComponent(components).toLegacyText());
+ }
// Paper end
}
@@ -1,124 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach.brown@destroystokyo.com>
Date: Mon, 29 Feb 2016 19:54:32 -0600
Subject: [PATCH] Graduate bungeecord chat API from spigot subclasses
Change Javadoc to be accurate
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index b53e94d79862d66165bbcd2a79b82770398a7f3e..9417030074fccdd9113bf75eb8f17f74b9a9118b 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -410,6 +410,30 @@ public final class Bukkit {
return server.broadcastMessage(message);
}
+ // Paper start
+ /**
+ * Sends the component to all online players.
+ *
+ * @param component the component to send
+ * @deprecated use {@code sendMessage} methods on {@link #getServer()} that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Deprecated
+ public static void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ server.broadcast(component);
+ }
+
+ /**
+ * Sends an array of components as a single message to all online players.
+ *
+ * @param components the components to send
+ * @deprecated use {@code sendMessage} methods on {@link #getServer()} that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Deprecated
+ public static void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ server.broadcast(components);
+ }
+ // Paper end
+
/**
* Gets the name of the update folder. The update folder is used to safely
* update plugins at the right moment on a plugin load.
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 124e88e512d24b6ff7ace3cf7d5a6adf4c2bb40d..ccc825a2ea43bb84a5a08dff00c4d8ec27e4e95a 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -345,6 +345,30 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
@Deprecated // Paper
public int broadcastMessage(@NotNull String message);
+ // Paper start
+ /**
+ * Sends the component to all online players.
+ *
+ * @param component the component to send
+ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Deprecated
+ public default void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ spigot().broadcast(component);
+ }
+
+ /**
+ * Sends an array of components as a single message to all online players.
+ *
+ * @param components the components to send
+ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Deprecated
+ public default void broadcast(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ spigot().broadcast(components);
+ }
+ // Paper end
+
/**
* Gets the name of the update folder. The update folder is used to safely
* update plugins at the right moment on a plugin load.
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index f8e45f70fe23cf73c8677b5d24cefcd3aae5e24e..f52e7d4e5b8399d04d77384f8fe5315086acd776 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -1041,6 +1041,42 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
*/
public void sendMap(@NotNull MapView map);
+ // Paper start
+ /**
+ * Sends the component to the player
+ *
+ * @param component the components to send
+ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Override
+ @Deprecated
+ public default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ spigot().sendMessage(component);
+ }
+
+ /**
+ * Sends an array of components as a single message to the player
+ *
+ * @param components the components to send
+ * @deprecated use {@code sendMessage} methods that accept {@link net.kyori.adventure.text.Component}
+ */
+ @Override
+ @Deprecated
+ public default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ spigot().sendMessage(components);
+ }
+
+ /**
+ * Sends an array of components as a single message to the specified screen position of this player
+ *
+ * @param position the screen position
+ * @param components the components to send
+ */
+ public default void sendMessage(net.md_5.bungee.api.ChatMessageType position, net.md_5.bungee.api.chat.BaseComponent... components) {
+ spigot().sendMessage(position, components);
+ }
+ // Paper end
+
/**
* Send a hurt animation. This fakes incoming damage towards the player from
* the given yaw relative to the player's direction.
@@ -1,580 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach.brown@destroystokyo.com>
Date: Mon, 29 Feb 2016 20:24:35 -0600
Subject: [PATCH] Add exception reporting event
diff --git a/src/main/java/com/destroystokyo/paper/event/server/ServerExceptionEvent.java b/src/main/java/com/destroystokyo/paper/event/server/ServerExceptionEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..306dbd975e9380c22dae0dad526725cc47a36f16
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/server/ServerExceptionEvent.java
@@ -0,0 +1,42 @@
+package com.destroystokyo.paper.event.server;
+
+import com.google.common.base.Preconditions;
+import org.bukkit.Bukkit;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import com.destroystokyo.paper.exception.ServerException;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Called whenever an exception is thrown in a recoverable section of the server.
+ */
+public class ServerExceptionEvent extends Event {
+ private static final HandlerList handlers = new HandlerList();
+ @NotNull private ServerException exception;
+
+ public ServerExceptionEvent(@NotNull ServerException exception) {
+ super(!Bukkit.isPrimaryThread());
+ this.exception = Preconditions.checkNotNull(exception, "exception");
+ }
+
+ /**
+ * Gets the wrapped exception that was thrown.
+ *
+ * @return Exception thrown
+ */
+ @NotNull
+ public ServerException getException() {
+ return exception;
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerCommandException.java b/src/main/java/com/destroystokyo/paper/exception/ServerCommandException.java
new file mode 100644
index 0000000000000000000000000000000000000000..6fb39af0479a818f7f1465bcdfe505ab4ff7da1a
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerCommandException.java
@@ -0,0 +1,64 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Thrown when a command throws an exception
+ */
+public class ServerCommandException extends ServerException {
+
+ private final Command command;
+ private final CommandSender commandSender;
+ private final String[] arguments;
+
+ public ServerCommandException(String message, Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
+ super(message, cause);
+ this.commandSender = checkNotNull(commandSender, "commandSender");
+ this.arguments = checkNotNull(arguments, "arguments");
+ this.command = checkNotNull(command, "command");
+ }
+
+ public ServerCommandException(Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
+ super(cause);
+ this.commandSender = checkNotNull(commandSender, "commandSender");
+ this.arguments = checkNotNull(arguments, "arguments");
+ this.command = checkNotNull(command, "command");
+ }
+
+ protected ServerCommandException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Command command, CommandSender commandSender, String[] arguments) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ this.commandSender = checkNotNull(commandSender, "commandSender");
+ this.arguments = checkNotNull(arguments, "arguments");
+ this.command = checkNotNull(command, "command");
+ }
+
+ /**
+ * Gets the command which threw the exception
+ *
+ * @return exception throwing command
+ */
+ public Command getCommand() {
+ return command;
+ }
+
+ /**
+ * Gets the command sender which executed the command request
+ *
+ * @return command sender of exception thrown command request
+ */
+ public CommandSender getCommandSender() {
+ return commandSender;
+ }
+
+ /**
+ * Gets the arguments which threw the exception for the command
+ *
+ * @return arguments of exception thrown command request
+ */
+ public String[] getArguments() {
+ return arguments;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerEventException.java b/src/main/java/com/destroystokyo/paper/exception/ServerEventException.java
new file mode 100644
index 0000000000000000000000000000000000000000..410b24139535cd5d8439ad581c43c61b5757fbf6
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerEventException.java
@@ -0,0 +1,52 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.event.Event;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.Plugin;
+
+import static com.google.common.base.Preconditions.*;
+
+/**
+ * Exception thrown when a server event listener throws an exception
+ */
+public class ServerEventException extends ServerPluginException {
+
+ private final Listener listener;
+ private final Event event;
+
+ public ServerEventException(String message, Throwable cause, Plugin responsiblePlugin, Listener listener, Event event) {
+ super(message, cause, responsiblePlugin);
+ this.listener = checkNotNull(listener, "listener");
+ this.event = checkNotNull(event, "event");
+ }
+
+ public ServerEventException(Throwable cause, Plugin responsiblePlugin, Listener listener, Event event) {
+ super(cause, responsiblePlugin);
+ this.listener = checkNotNull(listener, "listener");
+ this.event = checkNotNull(event, "event");
+ }
+
+ protected ServerEventException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin, Listener listener, Event event) {
+ super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
+ this.listener = checkNotNull(listener, "listener");
+ this.event = checkNotNull(event, "event");
+ }
+
+ /**
+ * Gets the listener which threw the exception
+ *
+ * @return event listener
+ */
+ public Listener getListener() {
+ return listener;
+ }
+
+ /**
+ * Gets the event which caused the exception
+ *
+ * @return event
+ */
+ public Event getEvent() {
+ return event;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerException.java b/src/main/java/com/destroystokyo/paper/exception/ServerException.java
new file mode 100644
index 0000000000000000000000000000000000000000..c06ea3942447d4824b83ff839cb449fb818dede1
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerException.java
@@ -0,0 +1,23 @@
+package com.destroystokyo.paper.exception;
+
+/**
+ * Wrapper exception for all exceptions that are thrown by the server.
+ */
+public class ServerException extends Exception {
+
+ public ServerException(String message) {
+ super(message);
+ }
+
+ public ServerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ServerException(Throwable cause) {
+ super(cause);
+ }
+
+ protected ServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerInternalException.java b/src/main/java/com/destroystokyo/paper/exception/ServerInternalException.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c3effca7c9d6c904cbe248d312b74e2cd360acf
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerInternalException.java
@@ -0,0 +1,36 @@
+package com.destroystokyo.paper.exception;
+
+import java.util.logging.Level;
+import org.bukkit.Bukkit;
+import com.destroystokyo.paper.event.server.ServerExceptionEvent;
+
+/**
+ * Thrown when the internal server throws a recoverable exception.
+ */
+public class ServerInternalException extends ServerException {
+
+ public ServerInternalException(String message) {
+ super(message);
+ }
+
+ public ServerInternalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ServerInternalException(Throwable cause) {
+ super(cause);
+ }
+
+ protected ServerInternalException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public static void reportInternalException(Throwable cause) {
+ try {
+ Bukkit.getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(cause)));
+ ;
+ } catch (Throwable t) {
+ Bukkit.getLogger().log(Level.WARNING, "Exception posting ServerExceptionEvent", t); // Don't want to rethrow!
+ }
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerPluginEnableDisableException.java b/src/main/java/com/destroystokyo/paper/exception/ServerPluginEnableDisableException.java
new file mode 100644
index 0000000000000000000000000000000000000000..f016ba3b1b62e554a9bacbb9635f2dbe441b3c4e
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerPluginEnableDisableException.java
@@ -0,0 +1,20 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.plugin.Plugin;
+
+/**
+ * Thrown whenever there is an exception with any enabling or disabling of plugins.
+ */
+public class ServerPluginEnableDisableException extends ServerPluginException {
+ public ServerPluginEnableDisableException(String message, Throwable cause, Plugin responsiblePlugin) {
+ super(message, cause, responsiblePlugin);
+ }
+
+ public ServerPluginEnableDisableException(Throwable cause, Plugin responsiblePlugin) {
+ super(cause, responsiblePlugin);
+ }
+
+ protected ServerPluginEnableDisableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin) {
+ super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerPluginException.java b/src/main/java/com/destroystokyo/paper/exception/ServerPluginException.java
new file mode 100644
index 0000000000000000000000000000000000000000..be3f92e3c6bcefe8b78da701b75121275001882e
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerPluginException.java
@@ -0,0 +1,36 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.plugin.Plugin;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Wrapper exception for all cases to which a plugin can be immediately blamed for
+ */
+public class ServerPluginException extends ServerException {
+ public ServerPluginException(String message, Throwable cause, Plugin responsiblePlugin) {
+ super(message, cause);
+ this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
+ }
+
+ public ServerPluginException(Throwable cause, Plugin responsiblePlugin) {
+ super(cause);
+ this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
+ }
+
+ protected ServerPluginException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
+ }
+
+ private final Plugin responsiblePlugin;
+
+ /**
+ * Gets the plugin which is directly responsible for the exception being thrown
+ *
+ * @return plugin which is responsible for the exception throw
+ */
+ public Plugin getResponsiblePlugin() {
+ return responsiblePlugin;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java b/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java
new file mode 100644
index 0000000000000000000000000000000000000000..2faef4cb358ec65e32a6aba6426f0dd7ddf90d2a
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java
@@ -0,0 +1,64 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
+
+import static com.google.common.base.Preconditions.*;
+
+/**
+ * Thrown when an incoming plugin message channel throws an exception
+ */
+public class ServerPluginMessageException extends ServerPluginException {
+
+ private final Player player;
+ private final String channel;
+ private final byte[] data;
+
+ public ServerPluginMessageException(String message, Throwable cause, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
+ super(message, cause, responsiblePlugin);
+ this.player = checkNotNull(player, "player");
+ this.channel = checkNotNull(channel, "channel");
+ this.data = checkNotNull(data, "data");
+ }
+
+ public ServerPluginMessageException(Throwable cause, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
+ super(cause, responsiblePlugin);
+ this.player = checkNotNull(player, "player");
+ this.channel = checkNotNull(channel, "channel");
+ this.data = checkNotNull(data, "data");
+ }
+
+ protected ServerPluginMessageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
+ super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
+ this.player = checkNotNull(player, "player");
+ this.channel = checkNotNull(channel, "channel");
+ this.data = checkNotNull(data, "data");
+ }
+
+ /**
+ * Gets the channel to which the error occurred from receiving data from
+ *
+ * @return exception channel
+ */
+ public String getChannel() {
+ return channel;
+ }
+
+ /**
+ * Gets the data to which the error occurred from
+ *
+ * @return exception data
+ */
+ public byte[] getData() {
+ return data;
+ }
+
+ /**
+ * Gets the player which the plugin message causing the exception originated from
+ *
+ * @return exception player
+ */
+ public Player getPlayer() {
+ return player;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerSchedulerException.java b/src/main/java/com/destroystokyo/paper/exception/ServerSchedulerException.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d0b2d4a9b3e5bdeec0e4ea7ab69858d86aa3715
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerSchedulerException.java
@@ -0,0 +1,37 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.scheduler.BukkitTask;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Thrown when a plugin's scheduler fails with an exception
+ */
+public class ServerSchedulerException extends ServerPluginException {
+
+ private final BukkitTask task;
+
+ public ServerSchedulerException(String message, Throwable cause, BukkitTask task) {
+ super(message, cause, task.getOwner());
+ this.task = checkNotNull(task, "task");
+ }
+
+ public ServerSchedulerException(Throwable cause, BukkitTask task) {
+ super(cause, task.getOwner());
+ this.task = checkNotNull(task, "task");
+ }
+
+ protected ServerSchedulerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, BukkitTask task) {
+ super(message, cause, enableSuppression, writableStackTrace, task.getOwner());
+ this.task = checkNotNull(task, "task");
+ }
+
+ /**
+ * Gets the task which threw the exception
+ *
+ * @return exception throwing task
+ */
+ public BukkitTask getTask() {
+ return task;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/exception/ServerTabCompleteException.java b/src/main/java/com/destroystokyo/paper/exception/ServerTabCompleteException.java
new file mode 100644
index 0000000000000000000000000000000000000000..5582999fe94c7a3dac655044ccc6d078cd9521a1
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerTabCompleteException.java
@@ -0,0 +1,22 @@
+package com.destroystokyo.paper.exception;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+/**
+ * Called when a tab-complete request throws an exception
+ */
+public class ServerTabCompleteException extends ServerCommandException {
+
+ public ServerTabCompleteException(String message, Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
+ super(message, cause, command, commandSender, arguments);
+ }
+
+ public ServerTabCompleteException(Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
+ super(cause, command, commandSender, arguments);
+ }
+
+ protected ServerTabCompleteException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Command command, CommandSender commandSender, String[] arguments) {
+ super(message, cause, enableSuppression, writableStackTrace, command, commandSender, arguments);
+ }
+}
diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java
index 4205649948a9e2a72f64c3f007112245abac6d50..b3b32ce429edbf1ed040354dbe28ab86f0d24201 100644
--- a/src/main/java/org/bukkit/command/SimpleCommandMap.java
+++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java
@@ -155,11 +155,14 @@ public class SimpleCommandMap implements CommandMap {
target.execute(sender, sentCommandLabel, Arrays.copyOfRange(args, 1, args.length));
} // target.timings.stopTiming(); // Spigot // Paper
} catch (CommandException ex) {
+ server.getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper
//target.timings.stopTiming(); // Spigot // Paper
throw ex;
} catch (Throwable ex) {
//target.timings.stopTiming(); // Spigot // Paper
- throw new CommandException("Unhandled exception executing '" + commandLine + "' in " + target, ex);
+ String msg = "Unhandled exception executing '" + commandLine + "' in " + target;
+ server.getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper
+ throw new CommandException(msg, ex);
}
// return true as command was handled
@@ -238,7 +241,9 @@ public class SimpleCommandMap implements CommandMap {
} catch (CommandException ex) {
throw ex;
} catch (Throwable ex) {
- throw new CommandException("Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target, ex);
+ String msg = "Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target;
+ server.getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerTabCompleteException(msg, ex, target, sender, args))); // Paper
+ throw new CommandException(msg, ex);
}
}
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index 974ff7116b294473ec450757e8a9341540f43dcd..fc2dae69165776d08274e34a69962cc70445f411 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -515,7 +515,8 @@ public final class SimplePluginManager implements PluginManager {
try {
plugin.getPluginLoader().enablePlugin(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while enabling "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin);
}
HandlerList.bakeAll();
@@ -538,32 +539,37 @@ public final class SimplePluginManager implements PluginManager {
try {
plugin.getPluginLoader().disablePlugin(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while disabling "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getScheduler().cancelTasks(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while cancelling tasks for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while cancelling tasks for "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getServicesManager().unregisterAll(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering services for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while unregistering services for "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
HandlerList.unregisterAll(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering events for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while unregistering events for "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getMessenger().unregisterIncomingPluginChannel(plugin);
server.getMessenger().unregisterOutgoingPluginChannel(plugin);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering plugin channels for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ handlePluginException("Error occurred (in the plugin loader) while unregistering plugin channels for "
+ + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
@@ -576,6 +582,13 @@ public final class SimplePluginManager implements PluginManager {
}
}
+ // Paper start
+ private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
+ server.getLogger().log(Level.SEVERE, msg, ex);
+ callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerPluginEnableDisableException(msg, ex, plugin)));
+ }
+ // Paper end
+
@Override
public void clearPlugins() {
if (true) {this.paperPluginManager.clearPlugins(); return;} // Paper
@@ -641,7 +654,13 @@ public final class SimplePluginManager implements PluginManager {
));
}
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(), ex);
+ // Paper start - error reporting
+ String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName();
+ server.getLogger().log(Level.SEVERE, msg, ex);
+ if (!(event instanceof com.destroystokyo.paper.event.server.ServerExceptionEvent)) { // We don't want to cause an endless event loop
+ callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
+ }
+ // Paper end
}
}
}
@@ -1,54 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kashike <kashike@vq.lc>
Date: Tue, 8 Mar 2016 13:05:59 -0800
Subject: [PATCH] Add BaseComponent sendMessage methods to CommandSender
diff --git a/src/main/java/org/bukkit/command/CommandSender.java b/src/main/java/org/bukkit/command/CommandSender.java
index 0f2b85e4979c478fbcc20bd67446e6ac8c8696d7..5c8ae3e0a927f2f2924d719b03499d936cf4d951 100644
--- a/src/main/java/org/bukkit/command/CommandSender.java
+++ b/src/main/java/org/bukkit/command/CommandSender.java
@@ -1,6 +1,9 @@
package org.bukkit.command;
import java.util.UUID;
+import net.kyori.adventure.audience.MessageType;
+import net.kyori.adventure.identity.Identity;
+import net.kyori.adventure.text.Component;
import org.bukkit.Server;
import org.bukkit.permissions.Permissible;
import org.jetbrains.annotations.NotNull;
@@ -168,5 +171,33 @@ public interface CommandSender extends net.kyori.adventure.audience.Audience, Pe
default void sendPlainMessage(final @NotNull String message) {
this.sendMessage(net.kyori.adventure.text.Component.text(message));
}
+
+ /**
+ * Sends the component to the sender
+ *
+ * <p>If this sender does not support sending full components then
+ * the component will be sent as legacy text.</p>
+ *
+ * @param component the component to send
+ * @deprecated use {@link #sendMessage(Identity, Component, MessageType)} instead
+ */
+ @Deprecated
+ default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent component) {
+ this.sendMessage(component.toLegacyText());
+ }
+
+ /**
+ * Sends an array of components as a single message to the sender
+ *
+ * <p>If this sender does not support sending full components then
+ * the components will be sent as legacy text.</p>
+ *
+ * @param components the components to send
+ * @deprecated use {@link #sendMessage(Identity, Component, MessageType)} instead
+ */
+ @Deprecated
+ default void sendMessage(@NotNull net.md_5.bungee.api.chat.BaseComponent... components) {
+ this.sendMessage(new net.md_5.bungee.api.chat.TextComponent(components).toLegacyText());
+ }
// Paper end
}
@@ -0,0 +1,577 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Techcable <Techcable@outlook.com>
Date: Mon, 29 Feb 2016 20:02:40 -0600
Subject: [PATCH] Player Tab List and Title APIs
Co-authored-by: Fruxz <cedricspitzer@outlook.de>
diff --git a/src/main/java/com/destroystokyo/paper/Title.java b/src/main/java/com/destroystokyo/paper/Title.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e90c3df567a65b48a0b9341f784eb902cb35d8c
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/Title.java
@@ -0,0 +1,420 @@
+package com.destroystokyo.paper;
+
+import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.TextComponent;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Represents a title to may be sent to a {@link Player}.
+ *
+ * <p>A title can be sent without subtitle text.</p>
+ *
+ * @deprecated use {@link net.kyori.adventure.title.Title}
+ */
+@Deprecated
+public final class Title {
+
+ /**
+ * The default number of ticks for the title to fade in.
+ */
+ public static final int DEFAULT_FADE_IN = 20;
+ /**
+ * The default number of ticks for the title to stay.
+ */
+ public static final int DEFAULT_STAY = 200;
+ /**
+ * The default number of ticks for the title to fade out.
+ */
+ public static final int DEFAULT_FADE_OUT = 20;
+
+ private final BaseComponent[] title;
+ private final BaseComponent[] subtitle;
+ private final int fadeIn;
+ private final int stay;
+ private final int fadeOut;
+
+ /**
+ * Create a title with the default time values and no subtitle.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @throws NullPointerException if the title is null
+ */
+ public Title(@NotNull BaseComponent title) {
+ this(title, null);
+ }
+
+ /**
+ * Create a title with the default time values and no subtitle.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @throws NullPointerException if the title is null
+ */
+ public Title(@NotNull BaseComponent[] title) {
+ this(title, null);
+ }
+
+ /**
+ * Create a title with the default time values and no subtitle.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @throws NullPointerException if the title is null
+ */
+ public Title(@NotNull String title) {
+ this(title, null);
+ }
+
+ /**
+ * Create a title with the default time values.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ */
+ public Title(@NotNull BaseComponent title, @Nullable BaseComponent subtitle) {
+ this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
+ }
+
+ /**
+ * Create a title with the default time values.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ */
+ public Title(@NotNull BaseComponent[] title, @Nullable BaseComponent[] subtitle) {
+ this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
+ }
+
+ /**
+ * Create a title with the default time values.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ */
+ public Title(@NotNull String title, @Nullable String subtitle) {
+ this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
+ }
+
+ /**
+ * Creates a new title.
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ * @param fadeIn the number of ticks for the title to fade in
+ * @param stay the number of ticks for the title to stay on screen
+ * @param fadeOut the number of ticks for the title to fade out
+ * @throws IllegalArgumentException if any of the times are negative
+ */
+ public Title(@NotNull BaseComponent title, @Nullable BaseComponent subtitle, int fadeIn, int stay, int fadeOut) {
+ this(
+ new BaseComponent[]{checkNotNull(title, "title")},
+ subtitle == null ? null : new BaseComponent[]{subtitle},
+ fadeIn,
+ stay,
+ fadeOut
+ );
+ }
+
+ /**
+ * Creates a new title.
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ * @param fadeIn the number of ticks for the title to fade in
+ * @param stay the number of ticks for the title to stay on screen
+ * @param fadeOut the number of ticks for the title to fade out
+ * @throws IllegalArgumentException if any of the times are negative
+ */
+ public Title(@Nullable BaseComponent[] title, @NotNull BaseComponent[] subtitle, int fadeIn, int stay, int fadeOut) {
+ checkArgument(fadeIn >= 0, "Negative fadeIn: %s", fadeIn);
+ checkArgument(stay >= 0, "Negative stay: %s", stay);
+ checkArgument(fadeOut >= 0, "Negative fadeOut: %s", fadeOut);
+ this.title = checkNotNull(title, "title");
+ this.subtitle = subtitle;
+ this.fadeIn = fadeIn;
+ this.stay = stay;
+ this.fadeOut = fadeOut;
+ }
+
+ /**
+ * Creates a new title.
+ *
+ * <p>It is recommended to the {@link BaseComponent} constrctors.</p>
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ * @param fadeIn the number of ticks for the title to fade in
+ * @param stay the number of ticks for the title to stay on screen
+ * @param fadeOut the number of ticks for the title to fade out
+ */
+ public Title(@NotNull String title, @Nullable String subtitle, int fadeIn, int stay, int fadeOut) {
+ this(
+ TextComponent.fromLegacyText(checkNotNull(title, "title")),
+ subtitle == null ? null : TextComponent.fromLegacyText(subtitle),
+ fadeIn,
+ stay,
+ fadeOut
+ );
+ }
+
+ /**
+ * Gets the text of this title
+ *
+ * @return the text
+ */
+ @NotNull
+ public BaseComponent[] getTitle() {
+ return this.title;
+ }
+
+ /**
+ * Gets the text of this title's subtitle
+ *
+ * @return the text
+ */
+ @Nullable
+ public BaseComponent[] getSubtitle() {
+ return this.subtitle;
+ }
+
+ /**
+ * Gets the number of ticks to fade in.
+ *
+ * <p>The returned value is never negative.</p>
+ *
+ * @return the number of ticks to fade in
+ */
+ public int getFadeIn() {
+ return this.fadeIn;
+ }
+
+ /**
+ * Gets the number of ticks to stay.
+ *
+ * <p>The returned value is never negative.</p>
+ *
+ * @return the number of ticks to stay
+ */
+ public int getStay() {
+ return this.stay;
+ }
+
+ /**
+ * Gets the number of ticks to fade out.
+ *
+ * <p>The returned value is never negative.</p>
+ *
+ * @return the number of ticks to fade out
+ */
+ public int getFadeOut() {
+ return this.fadeOut;
+ }
+
+ /**
+ * Sends the title directly to an player
+ *
+ * @param player the receiver of the title
+ */
+ public void send(@NotNull Player player) {
+ player.sendTitle(this);
+ }
+
+ /**
+ * Sends the title directly to the defined players
+ *
+ * @param players the receivers of the title
+ */
+ public void send(@NotNull Collection<? extends Player> players) {
+ for (Player player : players) {
+ player.sendTitle(this);
+ }
+ }
+
+ /**
+ * Sends the title directly to the defined players
+ *
+ * @param players the receivers of the title
+ */
+ public void send(@NotNull Player[] players) {
+ for (Player player : players) {
+ player.sendTitle(this);
+ }
+ }
+
+ /**
+ * Sends the title directly to all online players
+ */
+ public void broadcast() {
+ send(Bukkit.getOnlinePlayers());
+ }
+
+ @NotNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * A builder for creating titles
+ */
+ public static final class Builder {
+
+ private BaseComponent[] title;
+ private BaseComponent[] subtitle;
+ private int fadeIn = DEFAULT_FADE_IN;
+ private int stay = DEFAULT_STAY;
+ private int fadeOut = DEFAULT_FADE_OUT;
+
+ /**
+ * Sets the title to the given text.
+ *
+ * @param title the title text
+ * @return this builder instance
+ * @throws NullPointerException if the title is null
+ */
+ @NotNull
+ public Builder title(@NotNull BaseComponent title) {
+ return this.title(new BaseComponent[]{checkNotNull(title, "title")});
+ }
+
+ /**
+ * Sets the title to the given text.
+ *
+ * @param title the title text
+ * @return this builder instance
+ * @throws NullPointerException if the title is null
+ */
+ @NotNull
+ public Builder title(@NotNull BaseComponent[] title) {
+ this.title = checkNotNull(title, "title");
+ return this;
+ }
+
+ /**
+ * Sets the title to the given text.
+ *
+ * <p>It is recommended to the {@link BaseComponent} methods.</p>
+ *
+ * @param title the title text
+ * @return this builder instance
+ * @throws NullPointerException if the title is null
+ */
+ @NotNull
+ public Builder title(@NotNull String title) {
+ return this.title(TextComponent.fromLegacyText(checkNotNull(title, "title")));
+ }
+
+ /**
+ * Sets the subtitle to the given text.
+ *
+ * @param subtitle the title text
+ * @return this builder instance
+ */
+ @NotNull
+ public Builder subtitle(@Nullable BaseComponent subtitle) {
+ return this.subtitle(subtitle == null ? null : new BaseComponent[]{subtitle});
+ }
+
+ /**
+ * Sets the subtitle to the given text.
+ *
+ * @param subtitle the title text
+ * @return this builder instance
+ */
+ @NotNull
+ public Builder subtitle(@Nullable BaseComponent[] subtitle) {
+ this.subtitle = subtitle;
+ return this;
+ }
+
+ /**
+ * Sets the subtitle to the given text.
+ *
+ * <p>It is recommended to the {@link BaseComponent} methods.</p>
+ *
+ * @param subtitle the title text
+ * @return this builder instance
+ */
+ @NotNull
+ public Builder subtitle(@Nullable String subtitle) {
+ return this.subtitle(subtitle == null ? null : TextComponent.fromLegacyText(subtitle));
+ }
+
+ /**
+ * Sets the number of ticks for the title to fade in
+ *
+ * @param fadeIn the number of ticks to fade in
+ * @return this builder instance
+ * @throws IllegalArgumentException if it is negative
+ */
+ @NotNull
+ public Builder fadeIn(int fadeIn) {
+ checkArgument(fadeIn >= 0, "Negative fadeIn: %s", fadeIn);
+ this.fadeIn = fadeIn;
+ return this;
+ }
+
+
+ /**
+ * Sets the number of ticks for the title to stay.
+ *
+ * @param stay the number of ticks to stay
+ * @return this builder instance
+ * @throws IllegalArgumentException if it is negative
+ */
+ @NotNull
+ public Builder stay(int stay) {
+ checkArgument(stay >= 0, "Negative stay: %s", stay);
+ this.stay = stay;
+ return this;
+ }
+
+ /**
+ * Sets the number of ticks for the title to fade out.
+ *
+ * @param fadeOut the number of ticks to fade out
+ * @return this builder instance
+ * @throws IllegalArgumentException if it is negative
+ */
+ @NotNull
+ public Builder fadeOut(int fadeOut) {
+ checkArgument(fadeOut >= 0, "Negative fadeOut: %s", fadeOut);
+ this.fadeOut = fadeOut;
+ return this;
+ }
+
+ /**
+ * Create a title based on the values in the builder.
+ *
+ * @return a title from the values in this builder
+ * @throws IllegalStateException if title isn't specified
+ */
+ @NotNull
+ public Title build() {
+ checkState(title != null, "Title not specified");
+ return new Title(this.title, this.subtitle, this.fadeIn, this.stay, this.fadeOut);
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index b6a848261f7c136cbcb4a4f40223d2f248d546c2..1df2ff828aee7dac50d63fe1d51104e9fb2ed264 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -2,6 +2,7 @@ package org.bukkit.entity;
import java.net.InetSocketAddress;
import java.util.UUID;
+import com.destroystokyo.paper.Title; // Paper
import org.bukkit.DyeColor;
import org.bukkit.Effect;
import org.bukkit.GameMode;
@@ -742,6 +743,131 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
public default void sendMessage(net.md_5.bungee.api.ChatMessageType position, net.md_5.bungee.api.chat.BaseComponent... components) {
spigot().sendMessage(position, components);
}
+
+ /**
+ * Set the text displayed in the player list header and footer for this player
+ *
+ * @param header content for the top of the player list
+ * @param footer content for the bottom of the player list
+ * @deprecated in favour of {@link #sendPlayerListHeaderAndFooter(net.kyori.adventure.text.Component, net.kyori.adventure.text.Component)}
+ */
+ @Deprecated
+ public void setPlayerListHeaderFooter(@Nullable net.md_5.bungee.api.chat.BaseComponent[] header, @Nullable net.md_5.bungee.api.chat.BaseComponent[] footer);
+
+ /**
+ * Set the text displayed in the player list header and footer for this player
+ *
+ * @param header content for the top of the player list
+ * @param footer content for the bottom of the player list
+ * @deprecated in favour of {@link #sendPlayerListHeaderAndFooter(net.kyori.adventure.text.Component, net.kyori.adventure.text.Component)}
+ */
+ @Deprecated
+ public void setPlayerListHeaderFooter(@Nullable net.md_5.bungee.api.chat.BaseComponent header, @Nullable net.md_5.bungee.api.chat.BaseComponent footer);
+
+ /**
+ * Update the times for titles displayed to the player
+ *
+ * @param fadeInTicks ticks to fade-in
+ * @param stayTicks ticks to stay visible
+ * @param fadeOutTicks ticks to fade-out
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void setTitleTimes(int fadeInTicks, int stayTicks, int fadeOutTicks);
+
+ /**
+ * Update the subtitle of titles displayed to the player
+ *
+ * @param subtitle Subtitle to set
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void setSubtitle(net.md_5.bungee.api.chat.BaseComponent[] subtitle);
+
+ /**
+ * Update the subtitle of titles displayed to the player
+ *
+ * @param subtitle Subtitle to set
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void setSubtitle(net.md_5.bungee.api.chat.BaseComponent subtitle);
+
+ /**
+ * Show the given title to the player, along with the last subtitle set, using the last set times
+ *
+ * @param title Title to set
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent[] title);
+
+ /**
+ * Show the given title to the player, along with the last subtitle set, using the last set times
+ *
+ * @param title Title to set
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent title);
+
+ /**
+ * Show the given title and subtitle to the player using the given times
+ *
+ * @param title big text
+ * @param subtitle little text under it
+ * @param fadeInTicks ticks to fade-in
+ * @param stayTicks ticks to stay visible
+ * @param fadeOutTicks ticks to fade-out
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent[] title, @Nullable net.md_5.bungee.api.chat.BaseComponent[] subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks);
+
+ /**
+ * Show the given title and subtitle to the player using the given times
+ *
+ * @param title big text
+ * @param subtitle little text under it
+ * @param fadeInTicks ticks to fade-in
+ * @param stayTicks ticks to stay visible
+ * @param fadeOutTicks ticks to fade-out
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent title, @Nullable net.md_5.bungee.api.chat.BaseComponent subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks);
+
+ /**
+ * Show the title to the player, overriding any previously displayed title.
+ *
+ * <p>This method overrides any previous title, use {@link #updateTitle(Title)} to change the existing one.</p>
+ *
+ * @param title the title to send
+ * @throws NullPointerException if the title is null
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ void sendTitle(@NotNull Title title);
+
+ /**
+ * Show the title to the player, overriding any previously displayed title.
+ *
+ * <p>This method doesn't override previous titles, but changes their values.</p>
+ *
+ * @param title the title to send
+ * @throws NullPointerException if title is null
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ void updateTitle(@NotNull Title title);
+
+ /**
+ * Hide any title that is currently visible to the player
+ *
+ * @deprecated use {@link #clearTitle()}
+ */
+ @Deprecated
+ public void hideTitle();
// Paper end
/**
@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: mrapple <tony@oc.tc>
Date: Sun, 25 Nov 2012 13:47:27 -0600
Subject: [PATCH] Add methods for working with arrows stuck in living entities
diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
index 5077ec367a5cba88957c6115be27742974f7deec..b41133f23d25f90fc0993499056c4eeaf003a701 100644
--- a/src/main/java/org/bukkit/entity/LivingEntity.java
+++ b/src/main/java/org/bukkit/entity/LivingEntity.java
@@ -612,4 +612,19 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
* @return Whether the entity is invisible
*/
public boolean isInvisible();
+
+ // Paper start
+ /**
+ * Get the number of arrows stuck in this entity
+ * @return Number of arrows stuck
+ */
+ int getArrowsStuck();
+
+ /**
+ * Set the number of arrows stuck in this entity
+ *
+ * @param arrows Number of arrows to stick in this entity
+ */
+ void setArrowsStuck(int arrows);
+ // Paper end
}
@@ -0,0 +1,182 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jedediah Smith <jedediah@silencegreys.com>
Date: Sat, 4 Apr 2015 22:59:54 -0400
Subject: [PATCH] Complete resource pack API
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index cd2c4ff23cdbcc3e4e9ae94477a372d25ad31e39..422f322b9623dcb33472744ddfd91d7dba817471 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -1251,7 +1251,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* @throws IllegalArgumentException Thrown if the URL is null.
* @throws IllegalArgumentException Thrown if the URL is too long. The
* length restriction is an implementation specific arbitrary value.
+ * @deprecated use {@link #setResourcePack(String, String)}
*/
+ @Deprecated // Paper
public void setResourcePack(@NotNull String url);
/**
@@ -2025,6 +2027,124 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.displayName())));
}
+
+ /**
+ * Request that the player's client download and switch resource packs.
+ * <p>
+ * The player's client will download the new resource pack asynchronously
+ * in the background, and will automatically switch to it once the
+ * download is complete. If the client has downloaded and cached the same
+ * resource pack in the past, it will perform a quick timestamp check
+ * over the network to determine if the resource pack has changed and
+ * needs to be downloaded again. When this request is sent for the very
+ * first time from a given server, the client will first display a
+ * confirmation GUI to the player before proceeding with the download.
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>Players can disable server resources on their client, in which
+ * case this method will have no affect on them.
+ * <li>There is no concept of resetting resource packs back to default
+ * within Minecraft, so players will have to relog to do so.
+ * </ul>
+ *
+ * @param url The URL from which the client will download the resource
+ * pack. The string must contain only US-ASCII characters and should
+ * be encoded as per RFC 1738.
+ * @param hash A 40 character hexadecimal and lowercase SHA-1 digest of
+ * the resource pack file.
+ * @throws IllegalArgumentException Thrown if the URL is null.
+ * @throws IllegalArgumentException Thrown if the URL is too long. The
+ * length restriction is an implementation specific arbitrary value.
+ */
+ void setResourcePack(@NotNull String url, @NotNull String hash);
+
+ /**
+ * Request that the player's client download and switch resource packs.
+ * <p>
+ * The player's client will download the new resource pack asynchronously
+ * in the background, and will automatically switch to it once the
+ * download is complete. If the client has downloaded and cached the same
+ * resource pack in the past, it will perform a quick timestamp check
+ * over the network to determine if the resource pack has changed and
+ * needs to be downloaded again. When this request is sent for the very
+ * first time from a given server, the client will first display a
+ * confirmation GUI to the player before proceeding with the download.
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>Players can disable server resources on their client, in which
+ * case this method will have no affect on them.
+ * <li>There is no concept of resetting resource packs back to default
+ * within Minecraft, so players will have to relog to do so.
+ * </ul>
+ *
+ * @param url The URL from which the client will download the resource
+ * pack. The string must contain only US-ASCII characters and should
+ * be encoded as per RFC 1738.
+ * @param hash A 40 character hexadecimal and lowercase SHA-1 digest of
+ * the resource pack file.
+ * @param required Marks if the resource pack should be required by the client
+ * @throws IllegalArgumentException Thrown if the URL is null.
+ * @throws IllegalArgumentException Thrown if the URL is too long. The
+ * length restriction is an implementation specific arbitrary value.
+ */
+ void setResourcePack(@NotNull String url, @NotNull String hash, boolean required);
+
+ /**
+ * Request that the player's client download and switch resource packs.
+ * <p>
+ * The player's client will download the new resource pack asynchronously
+ * in the background, and will automatically switch to it once the
+ * download is complete. If the client has downloaded and cached the same
+ * resource pack in the past, it will perform a quick timestamp check
+ * over the network to determine if the resource pack has changed and
+ * needs to be downloaded again. When this request is sent for the very
+ * first time from a given server, the client will first display a
+ * confirmation GUI to the player before proceeding with the download.
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>Players can disable server resources on their client, in which
+ * case this method will have no affect on them.
+ * <li>There is no concept of resetting resource packs back to default
+ * within Minecraft, so players will have to relog to do so.
+ * </ul>
+ *
+ * @param url The URL from which the client will download the resource
+ * pack. The string must contain only US-ASCII characters and should
+ * be encoded as per RFC 1738.
+ * @param hash A 40 character hexadecimal and lowercase SHA-1 digest of
+ * the resource pack file.
+ * @param required Marks if the resource pack should be required by the client
+ * @param resourcePackPrompt A Prompt to be displayed in the client request
+ * @throws IllegalArgumentException Thrown if the URL is null.
+ * @throws IllegalArgumentException Thrown if the URL is too long. The
+ * length restriction is an implementation specific arbitrary value.
+ */
+ void setResourcePack(@NotNull String url, @NotNull String hash, boolean required, @Nullable net.kyori.adventure.text.Component resourcePackPrompt);
+ /**
+ * @return the most recent resource pack status received from the player,
+ * or null if no status has ever been received from this player.
+ */
+ @Nullable
+ org.bukkit.event.player.PlayerResourcePackStatusEvent.Status getResourcePackStatus();
+
+ /**
+ * @return the most recent resource pack hash received from the player,
+ * or null if no hash has ever been received from this player.
+ *
+ * @deprecated This is no longer sent from the client and will always be null
+ */
+ @Nullable
+ @Deprecated
+ String getResourcePackHash();
+
+ /**
+ * @return true if the last resource pack status received from this player
+ * was {@link org.bukkit.event.player.PlayerResourcePackStatusEvent.Status#SUCCESSFULLY_LOADED}
+ */
+ boolean hasResourcePack();
// Paper end
// Spigot start
diff --git a/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java b/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java
index b98195650d49d78ec35970ca0376b6289b861e4b..4c2102a11c3d682d98f0db4ccafa35231e66bcdd 100644
--- a/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java
@@ -11,13 +11,32 @@ import org.jetbrains.annotations.NotNull;
public class PlayerResourcePackStatusEvent extends PlayerEvent {
private static final HandlerList handlers = new HandlerList();
+ @Deprecated
+ private final String hash; // Paper
private final Status status;
public PlayerResourcePackStatusEvent(@NotNull final Player who, @NotNull Status resourcePackStatus) {
super(who);
+ this.hash = null; // Paper
this.status = resourcePackStatus;
}
+ @Deprecated // Paper
+ public PlayerResourcePackStatusEvent(final Player who, Status resourcePackStatus, String hash) {
+ super(who);
+ this.hash = hash; // Paper
+ this.status = resourcePackStatus;
+ }
+
+ @Deprecated
+ /**
+ * @deprecated Hash does not seem to ever be set
+ */
+ public String getHash() {
+ return this.hash;
+ }
+ // Paper end
+
/**
* Gets the status of this pack.
*
@@ -1,569 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Techcable <Techcable@outlook.com>
Date: Mon, 29 Feb 2016 20:02:40 -0600
Subject: [PATCH] Player Tab List and Title APIs
Co-authored-by: Fruxz <cedricspitzer@outlook.de>
diff --git a/src/main/java/com/destroystokyo/paper/Title.java b/src/main/java/com/destroystokyo/paper/Title.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e90c3df567a65b48a0b9341f784eb902cb35d8c
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/Title.java
@@ -0,0 +1,420 @@
+package com.destroystokyo.paper;
+
+import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.TextComponent;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Represents a title to may be sent to a {@link Player}.
+ *
+ * <p>A title can be sent without subtitle text.</p>
+ *
+ * @deprecated use {@link net.kyori.adventure.title.Title}
+ */
+@Deprecated
+public final class Title {
+
+ /**
+ * The default number of ticks for the title to fade in.
+ */
+ public static final int DEFAULT_FADE_IN = 20;
+ /**
+ * The default number of ticks for the title to stay.
+ */
+ public static final int DEFAULT_STAY = 200;
+ /**
+ * The default number of ticks for the title to fade out.
+ */
+ public static final int DEFAULT_FADE_OUT = 20;
+
+ private final BaseComponent[] title;
+ private final BaseComponent[] subtitle;
+ private final int fadeIn;
+ private final int stay;
+ private final int fadeOut;
+
+ /**
+ * Create a title with the default time values and no subtitle.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @throws NullPointerException if the title is null
+ */
+ public Title(@NotNull BaseComponent title) {
+ this(title, null);
+ }
+
+ /**
+ * Create a title with the default time values and no subtitle.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @throws NullPointerException if the title is null
+ */
+ public Title(@NotNull BaseComponent[] title) {
+ this(title, null);
+ }
+
+ /**
+ * Create a title with the default time values and no subtitle.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @throws NullPointerException if the title is null
+ */
+ public Title(@NotNull String title) {
+ this(title, null);
+ }
+
+ /**
+ * Create a title with the default time values.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ */
+ public Title(@NotNull BaseComponent title, @Nullable BaseComponent subtitle) {
+ this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
+ }
+
+ /**
+ * Create a title with the default time values.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ */
+ public Title(@NotNull BaseComponent[] title, @Nullable BaseComponent[] subtitle) {
+ this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
+ }
+
+ /**
+ * Create a title with the default time values.
+ *
+ * <p>Times use default values.</p>
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ */
+ public Title(@NotNull String title, @Nullable String subtitle) {
+ this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
+ }
+
+ /**
+ * Creates a new title.
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ * @param fadeIn the number of ticks for the title to fade in
+ * @param stay the number of ticks for the title to stay on screen
+ * @param fadeOut the number of ticks for the title to fade out
+ * @throws IllegalArgumentException if any of the times are negative
+ */
+ public Title(@NotNull BaseComponent title, @Nullable BaseComponent subtitle, int fadeIn, int stay, int fadeOut) {
+ this(
+ new BaseComponent[]{checkNotNull(title, "title")},
+ subtitle == null ? null : new BaseComponent[]{subtitle},
+ fadeIn,
+ stay,
+ fadeOut
+ );
+ }
+
+ /**
+ * Creates a new title.
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ * @param fadeIn the number of ticks for the title to fade in
+ * @param stay the number of ticks for the title to stay on screen
+ * @param fadeOut the number of ticks for the title to fade out
+ * @throws IllegalArgumentException if any of the times are negative
+ */
+ public Title(@Nullable BaseComponent[] title, @NotNull BaseComponent[] subtitle, int fadeIn, int stay, int fadeOut) {
+ checkArgument(fadeIn >= 0, "Negative fadeIn: %s", fadeIn);
+ checkArgument(stay >= 0, "Negative stay: %s", stay);
+ checkArgument(fadeOut >= 0, "Negative fadeOut: %s", fadeOut);
+ this.title = checkNotNull(title, "title");
+ this.subtitle = subtitle;
+ this.fadeIn = fadeIn;
+ this.stay = stay;
+ this.fadeOut = fadeOut;
+ }
+
+ /**
+ * Creates a new title.
+ *
+ * <p>It is recommended to the {@link BaseComponent} constrctors.</p>
+ *
+ * @param title the main text of the title
+ * @param subtitle the secondary text of the title
+ * @param fadeIn the number of ticks for the title to fade in
+ * @param stay the number of ticks for the title to stay on screen
+ * @param fadeOut the number of ticks for the title to fade out
+ */
+ public Title(@NotNull String title, @Nullable String subtitle, int fadeIn, int stay, int fadeOut) {
+ this(
+ TextComponent.fromLegacyText(checkNotNull(title, "title")),
+ subtitle == null ? null : TextComponent.fromLegacyText(subtitle),
+ fadeIn,
+ stay,
+ fadeOut
+ );
+ }
+
+ /**
+ * Gets the text of this title
+ *
+ * @return the text
+ */
+ @NotNull
+ public BaseComponent[] getTitle() {
+ return this.title;
+ }
+
+ /**
+ * Gets the text of this title's subtitle
+ *
+ * @return the text
+ */
+ @Nullable
+ public BaseComponent[] getSubtitle() {
+ return this.subtitle;
+ }
+
+ /**
+ * Gets the number of ticks to fade in.
+ *
+ * <p>The returned value is never negative.</p>
+ *
+ * @return the number of ticks to fade in
+ */
+ public int getFadeIn() {
+ return this.fadeIn;
+ }
+
+ /**
+ * Gets the number of ticks to stay.
+ *
+ * <p>The returned value is never negative.</p>
+ *
+ * @return the number of ticks to stay
+ */
+ public int getStay() {
+ return this.stay;
+ }
+
+ /**
+ * Gets the number of ticks to fade out.
+ *
+ * <p>The returned value is never negative.</p>
+ *
+ * @return the number of ticks to fade out
+ */
+ public int getFadeOut() {
+ return this.fadeOut;
+ }
+
+ /**
+ * Sends the title directly to an player
+ *
+ * @param player the receiver of the title
+ */
+ public void send(@NotNull Player player) {
+ player.sendTitle(this);
+ }
+
+ /**
+ * Sends the title directly to the defined players
+ *
+ * @param players the receivers of the title
+ */
+ public void send(@NotNull Collection<? extends Player> players) {
+ for (Player player : players) {
+ player.sendTitle(this);
+ }
+ }
+
+ /**
+ * Sends the title directly to the defined players
+ *
+ * @param players the receivers of the title
+ */
+ public void send(@NotNull Player[] players) {
+ for (Player player : players) {
+ player.sendTitle(this);
+ }
+ }
+
+ /**
+ * Sends the title directly to all online players
+ */
+ public void broadcast() {
+ send(Bukkit.getOnlinePlayers());
+ }
+
+ @NotNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * A builder for creating titles
+ */
+ public static final class Builder {
+
+ private BaseComponent[] title;
+ private BaseComponent[] subtitle;
+ private int fadeIn = DEFAULT_FADE_IN;
+ private int stay = DEFAULT_STAY;
+ private int fadeOut = DEFAULT_FADE_OUT;
+
+ /**
+ * Sets the title to the given text.
+ *
+ * @param title the title text
+ * @return this builder instance
+ * @throws NullPointerException if the title is null
+ */
+ @NotNull
+ public Builder title(@NotNull BaseComponent title) {
+ return this.title(new BaseComponent[]{checkNotNull(title, "title")});
+ }
+
+ /**
+ * Sets the title to the given text.
+ *
+ * @param title the title text
+ * @return this builder instance
+ * @throws NullPointerException if the title is null
+ */
+ @NotNull
+ public Builder title(@NotNull BaseComponent[] title) {
+ this.title = checkNotNull(title, "title");
+ return this;
+ }
+
+ /**
+ * Sets the title to the given text.
+ *
+ * <p>It is recommended to the {@link BaseComponent} methods.</p>
+ *
+ * @param title the title text
+ * @return this builder instance
+ * @throws NullPointerException if the title is null
+ */
+ @NotNull
+ public Builder title(@NotNull String title) {
+ return this.title(TextComponent.fromLegacyText(checkNotNull(title, "title")));
+ }
+
+ /**
+ * Sets the subtitle to the given text.
+ *
+ * @param subtitle the title text
+ * @return this builder instance
+ */
+ @NotNull
+ public Builder subtitle(@Nullable BaseComponent subtitle) {
+ return this.subtitle(subtitle == null ? null : new BaseComponent[]{subtitle});
+ }
+
+ /**
+ * Sets the subtitle to the given text.
+ *
+ * @param subtitle the title text
+ * @return this builder instance
+ */
+ @NotNull
+ public Builder subtitle(@Nullable BaseComponent[] subtitle) {
+ this.subtitle = subtitle;
+ return this;
+ }
+
+ /**
+ * Sets the subtitle to the given text.
+ *
+ * <p>It is recommended to the {@link BaseComponent} methods.</p>
+ *
+ * @param subtitle the title text
+ * @return this builder instance
+ */
+ @NotNull
+ public Builder subtitle(@Nullable String subtitle) {
+ return this.subtitle(subtitle == null ? null : TextComponent.fromLegacyText(subtitle));
+ }
+
+ /**
+ * Sets the number of ticks for the title to fade in
+ *
+ * @param fadeIn the number of ticks to fade in
+ * @return this builder instance
+ * @throws IllegalArgumentException if it is negative
+ */
+ @NotNull
+ public Builder fadeIn(int fadeIn) {
+ checkArgument(fadeIn >= 0, "Negative fadeIn: %s", fadeIn);
+ this.fadeIn = fadeIn;
+ return this;
+ }
+
+
+ /**
+ * Sets the number of ticks for the title to stay.
+ *
+ * @param stay the number of ticks to stay
+ * @return this builder instance
+ * @throws IllegalArgumentException if it is negative
+ */
+ @NotNull
+ public Builder stay(int stay) {
+ checkArgument(stay >= 0, "Negative stay: %s", stay);
+ this.stay = stay;
+ return this;
+ }
+
+ /**
+ * Sets the number of ticks for the title to fade out.
+ *
+ * @param fadeOut the number of ticks to fade out
+ * @return this builder instance
+ * @throws IllegalArgumentException if it is negative
+ */
+ @NotNull
+ public Builder fadeOut(int fadeOut) {
+ checkArgument(fadeOut >= 0, "Negative fadeOut: %s", fadeOut);
+ this.fadeOut = fadeOut;
+ return this;
+ }
+
+ /**
+ * Create a title based on the values in the builder.
+ *
+ * @return a title from the values in this builder
+ * @throws IllegalStateException if title isn't specified
+ */
+ @NotNull
+ public Title build() {
+ checkState(title != null, "Title not specified");
+ return new Title(this.title, this.subtitle, this.fadeIn, this.stay, this.fadeOut);
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index f52e7d4e5b8399d04d77384f8fe5315086acd776..3650fb2b79368ad39278062f215d25cc99637cac 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -1075,6 +1075,131 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
public default void sendMessage(net.md_5.bungee.api.ChatMessageType position, net.md_5.bungee.api.chat.BaseComponent... components) {
spigot().sendMessage(position, components);
}
+
+ /**
+ * Set the text displayed in the player list header and footer for this player
+ *
+ * @param header content for the top of the player list
+ * @param footer content for the bottom of the player list
+ * @deprecated in favour of {@link #sendPlayerListHeaderAndFooter(net.kyori.adventure.text.Component, net.kyori.adventure.text.Component)}
+ */
+ @Deprecated
+ public void setPlayerListHeaderFooter(@Nullable net.md_5.bungee.api.chat.BaseComponent[] header, @Nullable net.md_5.bungee.api.chat.BaseComponent[] footer);
+
+ /**
+ * Set the text displayed in the player list header and footer for this player
+ *
+ * @param header content for the top of the player list
+ * @param footer content for the bottom of the player list
+ * @deprecated in favour of {@link #sendPlayerListHeaderAndFooter(net.kyori.adventure.text.Component, net.kyori.adventure.text.Component)}
+ */
+ @Deprecated
+ public void setPlayerListHeaderFooter(@Nullable net.md_5.bungee.api.chat.BaseComponent header, @Nullable net.md_5.bungee.api.chat.BaseComponent footer);
+
+ /**
+ * Update the times for titles displayed to the player
+ *
+ * @param fadeInTicks ticks to fade-in
+ * @param stayTicks ticks to stay visible
+ * @param fadeOutTicks ticks to fade-out
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void setTitleTimes(int fadeInTicks, int stayTicks, int fadeOutTicks);
+
+ /**
+ * Update the subtitle of titles displayed to the player
+ *
+ * @param subtitle Subtitle to set
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void setSubtitle(net.md_5.bungee.api.chat.BaseComponent[] subtitle);
+
+ /**
+ * Update the subtitle of titles displayed to the player
+ *
+ * @param subtitle Subtitle to set
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void setSubtitle(net.md_5.bungee.api.chat.BaseComponent subtitle);
+
+ /**
+ * Show the given title to the player, along with the last subtitle set, using the last set times
+ *
+ * @param title Title to set
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent[] title);
+
+ /**
+ * Show the given title to the player, along with the last subtitle set, using the last set times
+ *
+ * @param title Title to set
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent title);
+
+ /**
+ * Show the given title and subtitle to the player using the given times
+ *
+ * @param title big text
+ * @param subtitle little text under it
+ * @param fadeInTicks ticks to fade-in
+ * @param stayTicks ticks to stay visible
+ * @param fadeOutTicks ticks to fade-out
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent[] title, @Nullable net.md_5.bungee.api.chat.BaseComponent[] subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks);
+
+ /**
+ * Show the given title and subtitle to the player using the given times
+ *
+ * @param title big text
+ * @param subtitle little text under it
+ * @param fadeInTicks ticks to fade-in
+ * @param stayTicks ticks to stay visible
+ * @param fadeOutTicks ticks to fade-out
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ public void showTitle(@Nullable net.md_5.bungee.api.chat.BaseComponent title, @Nullable net.md_5.bungee.api.chat.BaseComponent subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks);
+
+ /**
+ * Show the title to the player, overriding any previously displayed title.
+ *
+ * <p>This method overrides any previous title, use {@link #updateTitle(com.destroystokyo.paper.Title)} to change the existing one.</p>
+ *
+ * @param title the title to send
+ * @throws NullPointerException if the title is null
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ void sendTitle(@NotNull com.destroystokyo.paper.Title title);
+
+ /**
+ * Show the title to the player, overriding any previously displayed title.
+ *
+ * <p>This method doesn't override previous titles, but changes their values.</p>
+ *
+ * @param title the title to send
+ * @throws NullPointerException if title is null
+ * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} or {@link #sendTitlePart(net.kyori.adventure.title.TitlePart, Object)}
+ */
+ @Deprecated
+ void updateTitle(@NotNull com.destroystokyo.paper.Title title);
+
+ /**
+ * Hide any title that is currently visible to the player
+ *
+ * @deprecated use {@link #clearTitle()}
+ */
+ @Deprecated
+ public void hideTitle();
// Paper end
/**
@@ -1,65 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: mrapple <tony@oc.tc>
Date: Sun, 25 Nov 2012 13:47:27 -0600
Subject: [PATCH] Add methods for working with arrows stuck in living entities
Upstream added methods for this so the original methods
are now deprecated
diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java
index 7c82644de3c438f45476ac02ce2c51c6fdbd543a..390bb294d09389877937ee2c6c3f346e65dfb178 100644
--- a/src/main/java/org/bukkit/entity/LivingEntity.java
+++ b/src/main/java/org/bukkit/entity/LivingEntity.java
@@ -215,12 +215,26 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
*/
public int getArrowsInBody();
+ // Paper start
+ /**
+ * Set the amount of arrows in the entity's body.
+ * <p>
+ * Does not fire the {@link org.bukkit.event.entity.ArrowBodyCountChangeEvent}.
+ *
+ * @param count amount of arrows in entity's body
+ */
+ default void setArrowsInBody(final int count) {
+ this.setArrowsInBody(count, false);
+ }
+ // Paper end
+
/**
* Set the amount of arrows in the entity's body.
*
* @param count amount of arrows in entity's body
+ * @param fireEvent whether to fire the {@link org.bukkit.event.entity.ArrowBodyCountChangeEvent} event
*/
- public void setArrowsInBody(int count);
+ void setArrowsInBody(int count, boolean fireEvent); // Paper
/**
* Returns the living entity's current maximum no damage ticks.
@@ -722,4 +736,24 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
* @return Whether the entity is invisible
*/
public boolean isInvisible();
+
+ // Paper start
+ /**
+ * Get the number of arrows stuck in this entity
+ * @return Number of arrows stuck
+ * @deprecated use {@link #getArrowsInBody()}
+ */
+ @Deprecated
+ int getArrowsStuck();
+
+ /**
+ * Set the number of arrows stuck in this entity
+ *
+ * @param arrows Number of arrows to stick in this entity
+ * @deprecated use {@link #setArrowsInBody(int, boolean)}. <b>This method previously fired {@link org.bukkit.event.entity.ArrowBodyCountChangeEvent} so if
+ * you want to retain exact functionality, pass {@code true} for {@code fireEvent}.</b>
+ */
+ @Deprecated
+ void setArrowsStuck(int arrows);
+ // Paper end
}
@@ -0,0 +1,390 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Techcable <Techcable@outlook.com>
Date: Thu, 3 Mar 2016 13:20:33 -0700
Subject: [PATCH] Use ASM for event executors.
Uses method handles for private or static methods.
diff --git a/build.gradle.kts b/build.gradle.kts
index 0e8e827b5a05ac9423386eaea4188d132cc7f954..70d6503745942ea8637146369aeab8f9357c42d5 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -38,6 +38,9 @@ dependencies {
apiAndDocs("net.kyori:adventure-text-serializer-gson")
apiAndDocs("net.kyori:adventure-text-serializer-legacy")
apiAndDocs("net.kyori:adventure-text-serializer-plain")
+
+ implementation("org.ow2.asm:asm:9.2")
+ implementation("org.ow2.asm:asm-commons:9.2")
// Paper end
compileOnly("org.apache.maven:maven-resolver-provider:3.8.4")
diff --git a/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java b/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b28e9b1daba7834af67dbc193dd656bedd9a994
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java
@@ -0,0 +1,42 @@
+package com.destroystokyo.paper.event.executor;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+
+import com.destroystokyo.paper.util.SneakyThrow;
+import org.bukkit.event.Event;
+import org.bukkit.event.EventException;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.EventExecutor;
+import org.jetbrains.annotations.NotNull;
+
+public class MethodHandleEventExecutor implements EventExecutor {
+ private final Class<? extends Event> eventClass;
+ private final MethodHandle handle;
+
+ public MethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull MethodHandle handle) {
+ this.eventClass = eventClass;
+ this.handle = handle;
+ }
+
+ public MethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull Method m) {
+ this.eventClass = eventClass;
+ try {
+ m.setAccessible(true);
+ this.handle = MethodHandles.lookup().unreflect(m);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError("Unable to set accessible", e);
+ }
+ }
+
+ @Override
+ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
+ if (!eventClass.isInstance(event)) return;
+ try {
+ handle.invoke(listener, event);
+ } catch (Throwable t) {
+ SneakyThrow.sneaky(t);
+ }
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java b/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..c83672427324bd068ed52916f700b68446a226f6
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java
@@ -0,0 +1,43 @@
+package com.destroystokyo.paper.event.executor;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import com.destroystokyo.paper.util.SneakyThrow;
+import com.google.common.base.Preconditions;
+
+import org.bukkit.Bukkit;
+import org.bukkit.event.Event;
+import org.bukkit.event.EventException;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.EventExecutor;
+import org.jetbrains.annotations.NotNull;
+
+public class StaticMethodHandleEventExecutor implements EventExecutor {
+ private final Class<? extends Event> eventClass;
+ private final MethodHandle handle;
+
+ public StaticMethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull Method m) {
+ Preconditions.checkArgument(Modifier.isStatic(m.getModifiers()), "Not a static method: %s", m);
+ Preconditions.checkArgument(eventClass != null, "eventClass is null");
+ this.eventClass = eventClass;
+ try {
+ m.setAccessible(true);
+ this.handle = MethodHandles.lookup().unreflect(m);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError("Unable to set accessible", e);
+ }
+ }
+
+ @Override
+ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
+ if (!eventClass.isInstance(event)) return;
+ try {
+ handle.invoke(event);
+ } catch (Throwable throwable) {
+ SneakyThrow.sneaky(throwable);
+ }
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java b/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6e7d8ee8d903ebf975d60bec0e08603d9a49fdb
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java
@@ -0,0 +1,47 @@
+package com.destroystokyo.paper.event.executor.asm;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.bukkit.plugin.EventExecutor;
+import org.jetbrains.annotations.NotNull;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.GeneratorAdapter;
+
+import static org.objectweb.asm.Opcodes.*;
+
+public class ASMEventExecutorGenerator {
+ @NotNull
+ public static byte[] generateEventExecutor(@NotNull Method m, @NotNull String name) {
+ ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ writer.visit(V1_8, ACC_PUBLIC, name.replace('.', '/'), null, Type.getInternalName(Object.class), new String[] {Type.getInternalName(EventExecutor.class)});
+ // Generate constructor
+ GeneratorAdapter methodGenerator = new GeneratorAdapter(writer.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null), ACC_PUBLIC, "<init>", "()V");
+ methodGenerator.loadThis();
+ methodGenerator.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V", false); // Invoke the super class (Object) constructor
+ methodGenerator.returnValue();
+ methodGenerator.endMethod();
+ // Generate the execute method
+ methodGenerator = new GeneratorAdapter(writer.visitMethod(ACC_PUBLIC, "execute", "(Lorg/bukkit/event/Listener;Lorg/bukkit/event/Event;)V", null, null), ACC_PUBLIC, "execute", "(Lorg/bukkit/event/Listener;Lorg/bukkit/event/Listener;)V");;
+ methodGenerator.loadArg(0);
+ methodGenerator.checkCast(Type.getType(m.getDeclaringClass()));
+ methodGenerator.loadArg(1);
+ methodGenerator.checkCast(Type.getType(m.getParameterTypes()[0]));
+ methodGenerator.visitMethodInsn(m.getDeclaringClass().isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, Type.getInternalName(m.getDeclaringClass()), m.getName(), Type.getMethodDescriptor(m), m.getDeclaringClass().isInterface());
+ if (m.getReturnType() != void.class) {
+ methodGenerator.pop();
+ }
+ methodGenerator.returnValue();
+ methodGenerator.endMethod();
+ writer.visitEnd();
+ return writer.toByteArray();
+ }
+
+ public static AtomicInteger NEXT_ID = new AtomicInteger(1);
+ @NotNull
+ public static String generateName() {
+ int id = NEXT_ID.getAndIncrement();
+ return "com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor" + id;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java b/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java
new file mode 100644
index 0000000000000000000000000000000000000000..f79685b48bb581277a6891927988b6f7a4389dc4
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java
@@ -0,0 +1,34 @@
+package com.destroystokyo.paper.event.executor.asm;
+
+import org.jetbrains.annotations.NotNull;
+
+public interface ClassDefiner {
+
+ /**
+ * Returns if the defined classes can bypass access checks
+ *
+ * @return if classes bypass access checks
+ */
+ public default boolean isBypassAccessChecks() {
+ return false;
+ }
+
+ /**
+ * Define a class
+ *
+ * @param parentLoader the parent classloader
+ * @param name the name of the class
+ * @param data the class data to load
+ * @return the defined class
+ * @throws ClassFormatError if the class data is invalid
+ * @throws NullPointerException if any of the arguments are null
+ */
+ @NotNull
+ public Class<?> defineClass(@NotNull ClassLoader parentLoader, @NotNull String name, @NotNull byte[] data);
+
+ @NotNull
+ public static ClassDefiner getInstance() {
+ return SafeClassDefiner.INSTANCE;
+ }
+
+}
diff --git a/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java b/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac99477e9f2c08041aeff31abc1d1edee58d0a67
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java
@@ -0,0 +1,66 @@
+package com.destroystokyo.paper.event.executor.asm;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.google.common.base.Preconditions;
+
+import com.google.common.collect.MapMaker;
+import org.jetbrains.annotations.NotNull;
+import org.objectweb.asm.Type;
+
+public class SafeClassDefiner implements ClassDefiner {
+ /* default */ static final SafeClassDefiner INSTANCE = new SafeClassDefiner();
+
+ private SafeClassDefiner() {}
+
+ private final ConcurrentMap<ClassLoader, GeneratedClassLoader> loaders = new MapMaker().weakKeys().makeMap();
+
+ @NotNull
+ @Override
+ public Class<?> defineClass(@NotNull ClassLoader parentLoader, @NotNull String name, @NotNull byte[] data) {
+ GeneratedClassLoader loader = loaders.computeIfAbsent(parentLoader, GeneratedClassLoader::new);
+ synchronized (loader.getClassLoadingLock(name)) {
+ Preconditions.checkState(!loader.hasClass(name), "%s already defined", name);
+ Class<?> c = loader.define(name, data);
+ assert c.getName().equals(name);
+ return c;
+ }
+ }
+
+ private static class GeneratedClassLoader extends ClassLoader {
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
+
+ protected GeneratedClassLoader(@NotNull ClassLoader parent) {
+ super(parent);
+ }
+
+ private Class<?> define(@NotNull String name, byte[] data) {
+ synchronized (getClassLoadingLock(name)) {
+ assert !hasClass(name);
+ Class<?> c = defineClass(name, data, 0, data.length);
+ resolveClass(c);
+ return c;
+ }
+ }
+
+ @Override
+ @NotNull
+ public Object getClassLoadingLock(@NotNull String name) {
+ return super.getClassLoadingLock(name);
+ }
+
+ public boolean hasClass(@NotNull String name) {
+ synchronized (getClassLoadingLock(name)) {
+ try {
+ Class.forName(name);
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/plugin/EventExecutor.java b/src/main/java/org/bukkit/plugin/EventExecutor.java
index a850f0780de05463fc0d3f9e15ff7f19d88b2aed..9026e108ccd3a88aee1267ee275137befa646455 100644
--- a/src/main/java/org/bukkit/plugin/EventExecutor.java
+++ b/src/main/java/org/bukkit/plugin/EventExecutor.java
@@ -5,9 +5,75 @@ import org.bukkit.event.EventException;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
+// Paper start
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+
+import com.destroystokyo.paper.event.executor.MethodHandleEventExecutor;
+import com.destroystokyo.paper.event.executor.StaticMethodHandleEventExecutor;
+import com.destroystokyo.paper.event.executor.asm.ASMEventExecutorGenerator;
+import com.destroystokyo.paper.event.executor.asm.ClassDefiner;
+import com.google.common.base.Preconditions;
+// Paper end
+
/**
* Interface which defines the class for event call backs to plugins
*/
public interface EventExecutor {
public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException;
+
+ // Paper start
+ ConcurrentMap<Method, Class<? extends EventExecutor>> eventExecutorMap = new ConcurrentHashMap<Method, Class<? extends EventExecutor>>() {
+ @NotNull
+ @Override
+ public Class<? extends EventExecutor> computeIfAbsent(@NotNull Method key, @NotNull Function<? super Method, ? extends Class<? extends EventExecutor>> mappingFunction) {
+ Class<? extends EventExecutor> executorClass = get(key);
+ if (executorClass != null)
+ return executorClass;
+
+ //noinspection SynchronizationOnLocalVariableOrMethodParameter
+ synchronized (key) {
+ executorClass = get(key);
+ if (executorClass != null)
+ return executorClass;
+
+ return super.computeIfAbsent(key, mappingFunction);
+ }
+ }
+ };
+
+ @NotNull
+ public static EventExecutor create(@NotNull Method m, @NotNull Class<? extends Event> eventClass) {
+ Preconditions.checkNotNull(m, "Null method");
+ Preconditions.checkArgument(m.getParameterCount() != 0, "Incorrect number of arguments %s", m.getParameterCount());
+ Preconditions.checkArgument(m.getParameterTypes()[0] == eventClass, "First parameter %s doesn't match event class %s", m.getParameterTypes()[0], eventClass);
+ ClassDefiner definer = ClassDefiner.getInstance();
+ if (Modifier.isStatic(m.getModifiers())) {
+ return new StaticMethodHandleEventExecutor(eventClass, m);
+ } else if (definer.isBypassAccessChecks() || Modifier.isPublic(m.getDeclaringClass().getModifiers()) && Modifier.isPublic(m.getModifiers())) {
+ // get the existing generated EventExecutor class for the Method or generate one
+ Class<? extends EventExecutor> executorClass = eventExecutorMap.computeIfAbsent(m, (__) -> {
+ String name = ASMEventExecutorGenerator.generateName();
+ byte[] classData = ASMEventExecutorGenerator.generateEventExecutor(m, name);
+ return definer.defineClass(m.getDeclaringClass().getClassLoader(), name, classData).asSubclass(EventExecutor.class);
+ });
+
+ try {
+ EventExecutor asmExecutor = executorClass.newInstance();
+ // Define a wrapper to conform to bukkit stupidity (passing in events that don't match and wrapper exception)
+ return (listener, event) -> {
+ if (!eventClass.isInstance(event)) return;
+ asmExecutor.execute(listener, event);
+ };
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new AssertionError("Unable to initialize generated event executor", e);
+ }
+ } else {
+ return new MethodHandleEventExecutor(eventClass, m);
+ }
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
index 451fce7223834169751679ce6dc22de3df24eae9..0cbac77e16fc36d805b79dd44837a38a79618c0b 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
@@ -291,21 +291,7 @@ public final class JavaPluginLoader implements PluginLoader {
}
}
- EventExecutor executor = new co.aikar.timings.TimedEventExecutor(new EventExecutor() { // Paper
- @Override
- public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException { // Paper
- try {
- if (!eventClass.isAssignableFrom(event.getClass())) {
- return;
- }
- method.invoke(listener, event);
- } catch (InvocationTargetException ex) {
- throw new EventException(ex.getCause());
- } catch (Throwable t) {
- throw new EventException(t);
- }
- }
- }, plugin, method, eventClass); // Paper
+ EventExecutor executor = new co.aikar.timings.TimedEventExecutor(EventExecutor.create(method, eventClass), plugin, method, eventClass); // Paper // Paper (Yes.) - Use factory method `EventExecutor.create()`
if (false) { // Spigot - RL handles useTimings check now
eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled()));
} else {
@@ -1,182 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jedediah Smith <jedediah@silencegreys.com>
Date: Sat, 4 Apr 2015 22:59:54 -0400
Subject: [PATCH] Complete resource pack API
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
index 3650fb2b79368ad39278062f215d25cc99637cac..967ea421aed7f9d4c8edd366c00cf3ceca723b2f 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -1655,7 +1655,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* @throws IllegalArgumentException Thrown if the URL is null.
* @throws IllegalArgumentException Thrown if the URL is too long. The
* length restriction is an implementation specific arbitrary value.
+ * @deprecated use {@link #setResourcePack(String, String)}
*/
+ @Deprecated // Paper
public void setResourcePack(@NotNull String url);
/**
@@ -2500,6 +2502,124 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
default net.kyori.adventure.text.event.HoverEvent<net.kyori.adventure.text.event.HoverEvent.ShowEntity> asHoverEvent(final @NotNull java.util.function.UnaryOperator<net.kyori.adventure.text.event.HoverEvent.ShowEntity> op) {
return net.kyori.adventure.text.event.HoverEvent.showEntity(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowEntity.of(this.getType().getKey(), this.getUniqueId(), this.displayName())));
}
+
+ /**
+ * Request that the player's client download and switch resource packs.
+ * <p>
+ * The player's client will download the new resource pack asynchronously
+ * in the background, and will automatically switch to it once the
+ * download is complete. If the client has downloaded and cached the same
+ * resource pack in the past, it will perform a quick timestamp check
+ * over the network to determine if the resource pack has changed and
+ * needs to be downloaded again. When this request is sent for the very
+ * first time from a given server, the client will first display a
+ * confirmation GUI to the player before proceeding with the download.
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>Players can disable server resources on their client, in which
+ * case this method will have no affect on them.
+ * <li>There is no concept of resetting resource packs back to default
+ * within Minecraft, so players will have to relog to do so.
+ * </ul>
+ *
+ * @param url The URL from which the client will download the resource
+ * pack. The string must contain only US-ASCII characters and should
+ * be encoded as per RFC 1738.
+ * @param hash A 40 character hexadecimal and lowercase SHA-1 digest of
+ * the resource pack file.
+ * @throws IllegalArgumentException Thrown if the URL is null.
+ * @throws IllegalArgumentException Thrown if the URL is too long. The
+ * length restriction is an implementation specific arbitrary value.
+ */
+ void setResourcePack(@NotNull String url, @NotNull String hash);
+
+ /**
+ * Request that the player's client download and switch resource packs.
+ * <p>
+ * The player's client will download the new resource pack asynchronously
+ * in the background, and will automatically switch to it once the
+ * download is complete. If the client has downloaded and cached the same
+ * resource pack in the past, it will perform a quick timestamp check
+ * over the network to determine if the resource pack has changed and
+ * needs to be downloaded again. When this request is sent for the very
+ * first time from a given server, the client will first display a
+ * confirmation GUI to the player before proceeding with the download.
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>Players can disable server resources on their client, in which
+ * case this method will have no affect on them.
+ * <li>There is no concept of resetting resource packs back to default
+ * within Minecraft, so players will have to relog to do so.
+ * </ul>
+ *
+ * @param url The URL from which the client will download the resource
+ * pack. The string must contain only US-ASCII characters and should
+ * be encoded as per RFC 1738.
+ * @param hash A 40 character hexadecimal and lowercase SHA-1 digest of
+ * the resource pack file.
+ * @param required Marks if the resource pack should be required by the client
+ * @throws IllegalArgumentException Thrown if the URL is null.
+ * @throws IllegalArgumentException Thrown if the URL is too long. The
+ * length restriction is an implementation specific arbitrary value.
+ */
+ void setResourcePack(@NotNull String url, @NotNull String hash, boolean required);
+
+ /**
+ * Request that the player's client download and switch resource packs.
+ * <p>
+ * The player's client will download the new resource pack asynchronously
+ * in the background, and will automatically switch to it once the
+ * download is complete. If the client has downloaded and cached the same
+ * resource pack in the past, it will perform a quick timestamp check
+ * over the network to determine if the resource pack has changed and
+ * needs to be downloaded again. When this request is sent for the very
+ * first time from a given server, the client will first display a
+ * confirmation GUI to the player before proceeding with the download.
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>Players can disable server resources on their client, in which
+ * case this method will have no affect on them.
+ * <li>There is no concept of resetting resource packs back to default
+ * within Minecraft, so players will have to relog to do so.
+ * </ul>
+ *
+ * @param url The URL from which the client will download the resource
+ * pack. The string must contain only US-ASCII characters and should
+ * be encoded as per RFC 1738.
+ * @param hash A 40 character hexadecimal and lowercase SHA-1 digest of
+ * the resource pack file.
+ * @param required Marks if the resource pack should be required by the client
+ * @param resourcePackPrompt A Prompt to be displayed in the client request
+ * @throws IllegalArgumentException Thrown if the URL is null.
+ * @throws IllegalArgumentException Thrown if the URL is too long. The
+ * length restriction is an implementation specific arbitrary value.
+ */
+ void setResourcePack(@NotNull String url, @NotNull String hash, boolean required, @Nullable net.kyori.adventure.text.Component resourcePackPrompt);
+ /**
+ * @return the most recent resource pack status received from the player,
+ * or null if no status has ever been received from this player.
+ */
+ @Nullable
+ org.bukkit.event.player.PlayerResourcePackStatusEvent.Status getResourcePackStatus();
+
+ /**
+ * @return the most recent resource pack hash received from the player,
+ * or null if no hash has ever been received from this player.
+ *
+ * @deprecated This is no longer sent from the client and will always be null
+ */
+ @Nullable
+ @Deprecated
+ String getResourcePackHash();
+
+ /**
+ * @return true if the last resource pack status received from this player
+ * was {@link org.bukkit.event.player.PlayerResourcePackStatusEvent.Status#SUCCESSFULLY_LOADED}
+ */
+ boolean hasResourcePack();
// Paper end
// Spigot start
diff --git a/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java b/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java
index b98195650d49d78ec35970ca0376b6289b861e4b..4c2102a11c3d682d98f0db4ccafa35231e66bcdd 100644
--- a/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerResourcePackStatusEvent.java
@@ -11,13 +11,32 @@ import org.jetbrains.annotations.NotNull;
public class PlayerResourcePackStatusEvent extends PlayerEvent {
private static final HandlerList handlers = new HandlerList();
+ @Deprecated
+ private final String hash; // Paper
private final Status status;
public PlayerResourcePackStatusEvent(@NotNull final Player who, @NotNull Status resourcePackStatus) {
super(who);
+ this.hash = null; // Paper
this.status = resourcePackStatus;
}
+ @Deprecated // Paper
+ public PlayerResourcePackStatusEvent(final Player who, Status resourcePackStatus, String hash) {
+ super(who);
+ this.hash = hash; // Paper
+ this.status = resourcePackStatus;
+ }
+
+ @Deprecated
+ /**
+ * @deprecated Hash does not seem to ever be set
+ */
+ public String getHash() {
+ return this.hash;
+ }
+ // Paper end
+
/**
* Gets the status of this pack.
*
@@ -0,0 +1,104 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William <admin@domnian.com>
Date: Fri, 18 Mar 2016 03:28:07 -0400
Subject: [PATCH] Add command to reload permissions.yml and require confirm to
reload
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index 5fc71e24f1562ff0d448b964f29fd109d4d9bb4d..191e9985fc17ffbdb9b6bdadbea46da54f5f7599 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -1947,6 +1947,13 @@ public final class Bukkit {
public static org.bukkit.command.CommandMap getCommandMap() {
return server.getCommandMap();
}
+
+ /**
+ * Reload the Permissions in permissions.yml
+ */
+ public static void reloadPermissions() {
+ server.reloadPermissions();
+ }
// Paper end
@NotNull
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 8fb6545406ab6029d82c903856bda6c6d6fa0636..731679a0893635f5cb71a19fd9ee8562795230b8 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -1717,4 +1717,6 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
@NotNull
Spigot spigot();
// Spigot end
+
+ void reloadPermissions(); // Paper
}
diff --git a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
index 50cc311be7904cc8fc6070a21c8e4de3a489fd20..c62da4131b17e66892678e8b618fb9ba3de93b56 100644
--- a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
+++ b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
@@ -13,15 +13,35 @@ public class ReloadCommand extends BukkitCommand {
public ReloadCommand(@NotNull String name) {
super(name);
this.description = "Reloads the server configuration and plugins";
- this.usageMessage = "/reload";
+ this.usageMessage = "/reload [permissions]"; // Paper
this.setPermission("bukkit.command.reload");
this.setAliases(Arrays.asList("rl"));
}
@Override
- public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) {
+ public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) { // Paper
if (!testPermission(sender)) return true;
+ // Paper start - Reload permissions.yml & require confirm
+ boolean confirmed = System.getProperty("LetMeReload") != null;
+ if (args.length == 1) {
+ if (args[0].equalsIgnoreCase("permissions")) {
+ Bukkit.getServer().reloadPermissions();
+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Permissions successfully reloaded.");
+ return true;
+ } else if ("confirm".equalsIgnoreCase(args[0])) {
+ confirmed = true;
+ } else {
+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Usage: " + usageMessage);
+ return true;
+ }
+ }
+ if (!confirmed) {
+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Are you sure you wish to reload your server? Doing so may cause bugs and memory leaks. It is recommended to restart instead of using /reload. To confirm, please type " + ChatColor.YELLOW + "/reload confirm");
+ return true;
+ }
+ // Paper end
+
Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues when using some plugins.");
Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
Bukkit.reload();
@@ -33,6 +53,6 @@ public class ReloadCommand extends BukkitCommand {
@NotNull
@Override
public List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
- return Collections.emptyList();
+ return java.util.Collections.singletonList("permissions"); // Paper
}
}
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index 1fbf690403eefe017b146c141c6d47d9b8d9d4bb..3ffe519b759f440f56f3054b0d3e4120b59f8c62 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -905,4 +905,13 @@ public final class SimplePluginManager implements PluginManager {
public void useTimings(boolean use) {
co.aikar.timings.Timings.setTimingsEnabled(use); // Paper
}
+
+ // Paper start
+ public void clearPermissions() {
+ permissions.clear();
+ defaultPerms.get(true).clear();
+ defaultPerms.get(false).clear();
+ }
+ // Paper end
+
}
@@ -1,86 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William <admin@domnian.com>
Date: Fri, 18 Mar 2016 03:28:07 -0400
Subject: [PATCH] Add command to reload permissions.yml and require confirm to
reload
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index 88cbb61a855d77fe6840be48140261032c5a38b8..540c57e82247eaad2cae1ab0199caf2a6cabcab1 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -2200,6 +2200,13 @@ public final class Bukkit {
public static org.bukkit.command.CommandMap getCommandMap() {
return server.getCommandMap();
}
+
+ /**
+ * Reload the Permissions in permissions.yml
+ */
+ public static void reloadPermissions() {
+ server.reloadPermissions();
+ }
// Paper end
@NotNull
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 7edaadd0a99fd8bbe3c683a126efd29993ed596c..f3452f65e6b69e8a2f0942c7a6afb9837a6d88fd 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -1943,4 +1943,6 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
@NotNull
Spigot spigot();
// Spigot end
+
+ void reloadPermissions(); // Paper
}
diff --git a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
index 50cc311be7904cc8fc6070a21c8e4de3a489fd20..5fa9d648bc780e874f658597f1a24715bccac5cb 100644
--- a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
+++ b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java
@@ -13,15 +13,35 @@ public class ReloadCommand extends BukkitCommand {
public ReloadCommand(@NotNull String name) {
super(name);
this.description = "Reloads the server configuration and plugins";
- this.usageMessage = "/reload";
+ this.usageMessage = "/reload [permissions]"; // Paper
this.setPermission("bukkit.command.reload");
this.setAliases(Arrays.asList("rl"));
}
@Override
- public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) {
+ public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) { // Paper
if (!testPermission(sender)) return true;
+ // Paper start - Reload permissions.yml & require confirm
+ boolean confirmed = System.getProperty("LetMeReload") != null;
+ if (args.length == 1) {
+ if (args[0].equalsIgnoreCase("permissions")) {
+ Bukkit.getServer().reloadPermissions();
+ Command.broadcastCommandMessage(sender, net.kyori.adventure.text.Component.text("Permissions successfully reloaded.", net.kyori.adventure.text.format.NamedTextColor.GREEN));
+ return true;
+ } else if ("confirm".equalsIgnoreCase(args[0])) {
+ confirmed = true;
+ } else {
+ Command.broadcastCommandMessage(sender, net.kyori.adventure.text.Component.text("Usage: " + usageMessage, net.kyori.adventure.text.format.NamedTextColor.RED));
+ return true;
+ }
+ }
+ if (!confirmed) {
+ Command.broadcastCommandMessage(sender, net.kyori.adventure.text.Component.text("Are you sure you wish to reload your server? Doing so may cause bugs and memory leaks. It is recommended to restart instead of using /reload. To confirm, please type ", net.kyori.adventure.text.format.NamedTextColor.RED).append(net.kyori.adventure.text.Component.text("/reload confirm", net.kyori.adventure.text.format.NamedTextColor.YELLOW)));
+ return true;
+ }
+ // Paper end
+
Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues when using some plugins.");
Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
Bukkit.reload();
@@ -33,6 +53,6 @@ public class ReloadCommand extends BukkitCommand {
@NotNull
@Override
public List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
- return Collections.emptyList();
+ return java.util.Collections.singletonList("permissions"); // Paper
}
}
@@ -0,0 +1,48 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jedediah Smith <jedediah@silencegreys.com>
Date: Sun, 21 Jun 2015 15:05:21 -0400
Subject: [PATCH] Custom replacement for eaten items
diff --git a/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java b/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java
index c2793f3ef01c1246c130971c17e1c2bf8f551435..373f4b5b5185aa81ff728da89c9cc4e0ccf87889 100644
--- a/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java
@@ -22,6 +22,7 @@ public class PlayerItemConsumeEvent extends PlayerEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean isCancelled = false;
private ItemStack item;
+ @Nullable private ItemStack replacement; // Paper
/**
* @param player the player consuming
@@ -58,6 +59,29 @@ public class PlayerItemConsumeEvent extends PlayerEvent implements Cancellable {
}
}
+ // Paper start
+ /**
+ * Return the custom item stack that will replace the consumed item, or null if no
+ * custom replacement has been set (which means the default replacement will be used).
+ *
+ * @return The custom item stack that will replace the consumed item or null
+ */
+ @Nullable
+ public ItemStack getReplacement() {
+ return this.replacement;
+ }
+
+ /**
+ * Set a custom item stack to replace the consumed item. Pass null to clear any custom
+ * stack that has been set and use the default replacement.
+ *
+ * @param replacement Replacement item to set, null to clear any custom stack and use default
+ */
+ public void setReplacement(@Nullable ItemStack replacement) {
+ this.replacement = replacement;
+ }
+ // Paper end
+
@Override
public boolean isCancelled() {
return this.isCancelled;
@@ -1,48 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jedediah Smith <jedediah@silencegreys.com>
Date: Sun, 21 Jun 2015 15:05:21 -0400
Subject: [PATCH] Custom replacement for eaten items
diff --git a/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java b/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java
index 5c054eb531e3caf17c179aff6a712fb8c33d8f77..6110d0417340710333b44312d82c7bd5165a8954 100644
--- a/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerItemConsumeEvent.java
@@ -24,6 +24,7 @@ public class PlayerItemConsumeEvent extends PlayerEvent implements Cancellable {
private boolean isCancelled = false;
private ItemStack item;
private final EquipmentSlot hand;
+ @Nullable private ItemStack replacement; // Paper
/**
* @param player the player consuming
@@ -82,6 +83,29 @@ public class PlayerItemConsumeEvent extends PlayerEvent implements Cancellable {
return hand;
}
+ // Paper start
+ /**
+ * Return the custom item stack that will replace the consumed item, or null if no
+ * custom replacement has been set (which means the default replacement will be used).
+ *
+ * @return The custom item stack that will replace the consumed item or null
+ */
+ @Nullable
+ public ItemStack getReplacement() {
+ return this.replacement;
+ }
+
+ /**
+ * Set a custom item stack to replace the consumed item. Pass null to clear any custom
+ * stack that has been set and use the default replacement.
+ *
+ * @param replacement Replacement item to set, null to clear any custom stack and use default
+ */
+ public void setReplacement(@Nullable ItemStack replacement) {
+ this.replacement = replacement;
+ }
+ // Paper end
+
@Override
public boolean isCancelled() {
return this.isCancelled;
@@ -0,0 +1,90 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: crast <contact@jamescrasta.com>
Date: Sat, 1 Jun 2013 13:52:30 -0600
Subject: [PATCH] Reduce thread synchronization in MetadataStoreBase
Use ConcurrentHashMap to allow thread-safe access methods and very
limited synchronized portions to allow much higher concurrency in
MetadataStore as well as far less locking, especially on reads
diff --git a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
index baf850226aed8545a5794deba6dff9603953b4b2..d363d517c05b3335101d829ce4ec22d049059c24 100644
--- a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
+++ b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
@@ -12,7 +12,7 @@ import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
public abstract class MetadataStoreBase<T> {
- private Map<String, Map<Plugin, MetadataValue>> metadataMap = new HashMap<String, Map<Plugin, MetadataValue>>();
+ private Map<String, Map<Plugin, MetadataValue>> metadataMap = new java.util.concurrent.ConcurrentHashMap<String, Map<Plugin, MetadataValue>>(); // Paper
/**
* Adds a metadata value to an object. Each metadata value is owned by a
@@ -46,7 +46,9 @@ public abstract class MetadataStoreBase<T> {
entry = new WeakHashMap<Plugin, MetadataValue>(1);
metadataMap.put(key, entry);
}
- entry.put(owningPlugin, newMetadataValue);
+ synchronized (entry) {
+ entry.put(owningPlugin, newMetadataValue);
+ }
}
/**
@@ -60,10 +62,11 @@ public abstract class MetadataStoreBase<T> {
* @see MetadataStore#getMetadata(Object, String)
*/
@NotNull
- public synchronized List<MetadataValue> getMetadata(@NotNull T subject, @NotNull String metadataKey) {
+ public List<MetadataValue> getMetadata(@NotNull T subject, @NotNull String metadataKey) { // Paper
String key = disambiguate(subject, metadataKey);
- if (metadataMap.containsKey(key)) {
- Collection<MetadataValue> values = metadataMap.get(key).values();
+ Map<Plugin, MetadataValue> entry = metadataMap.get(key);
+ if (entry != null) {
+ Collection<MetadataValue> values = entry.values();
return Collections.unmodifiableList(new ArrayList<MetadataValue>(values));
} else {
return Collections.emptyList();
@@ -78,7 +81,7 @@ public abstract class MetadataStoreBase<T> {
* @param metadataKey the unique metadata key being queried.
* @return the existence of the metadataKey within subject.
*/
- public synchronized boolean hasMetadata(@NotNull T subject, @NotNull String metadataKey) {
+ public boolean hasMetadata(@NotNull T subject, @NotNull String metadataKey) { // Paper
String key = disambiguate(subject, metadataKey);
return metadataMap.containsKey(key);
}
@@ -94,17 +97,18 @@ public abstract class MetadataStoreBase<T> {
* @see MetadataStore#removeMetadata(Object, String,
* org.bukkit.plugin.Plugin)
*/
- public synchronized void removeMetadata(@NotNull T subject, @NotNull String metadataKey, @NotNull Plugin owningPlugin) {
+ public void removeMetadata(@NotNull T subject, @NotNull String metadataKey, @NotNull Plugin owningPlugin) { // Paper
Validate.notNull(owningPlugin, "Plugin cannot be null");
String key = disambiguate(subject, metadataKey);
Map<Plugin, MetadataValue> entry = metadataMap.get(key);
if (entry == null) {
return;
}
-
- entry.remove(owningPlugin);
- if (entry.isEmpty()) {
- metadataMap.remove(key);
+ synchronized (entry) {
+ entry.remove(owningPlugin);
+ if (entry.isEmpty()) {
+ metadataMap.remove(key);
+ }
}
}
@@ -117,7 +121,7 @@ public abstract class MetadataStoreBase<T> {
* @throws IllegalArgumentException If plugin is null
* @see MetadataStore#invalidateAll(org.bukkit.plugin.Plugin)
*/
- public synchronized void invalidateAll(@NotNull Plugin owningPlugin) {
+ public void invalidateAll(@NotNull Plugin owningPlugin) { // Paper
Validate.notNull(owningPlugin, "Plugin cannot be null");
for (Map<Plugin, MetadataValue> values : metadataMap.values()) {
if (values.containsKey(owningPlugin)) {
@@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 16 Jul 2013 21:26:50 -0400
Subject: [PATCH] Add MetadataStoreBase.removeAll(Plugin)
So that on reload, metadata will be cleared
diff --git a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
index d363d517c05b3335101d829ce4ec22d049059c24..abbe545af572687a0399c2387434863cd2b70f68 100644
--- a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
+++ b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator; // Paper
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -130,6 +131,26 @@ public abstract class MetadataStoreBase<T> {
}
}
+ /**
+ * Removes all metadata in the metadata store that originates from the
+ * given plugin.
+ *
+ * @param owningPlugin the plugin requesting the invalidation.
+ * @throws IllegalArgumentException If plugin is null
+ */
+ public void removeAll(@NotNull Plugin owningPlugin) {
+ Validate.notNull(owningPlugin, "Plugin cannot be null");
+ for (Iterator<Map<Plugin, MetadataValue>> iterator = metadataMap.values().iterator(); iterator.hasNext(); ) {
+ Map<Plugin, MetadataValue> values = iterator.next();
+ if (values.containsKey(owningPlugin)) {
+ values.remove(owningPlugin);
+ }
+ if (values.isEmpty()) {
+ iterator.remove();
+ }
+ }
+ }
+
/**
* Creates a unique name for the object receiving metadata by combining
* unique data from the subject with a metadataKey.
@@ -1,90 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: crast <contact@jamescrasta.com>
Date: Sat, 1 Jun 2013 13:52:30 -0600
Subject: [PATCH] Reduce thread synchronization in MetadataStoreBase
Use ConcurrentHashMap to allow thread-safe access methods and very
limited synchronized portions to allow much higher concurrency in
MetadataStore as well as far less locking, especially on reads
diff --git a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
index 6886a7003fb76d07e0fa9c1c352c56480f08a0db..a0698522f01619c9d37474f4bd55a17de58fff89 100644
--- a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
+++ b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
@@ -12,7 +12,7 @@ import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
public abstract class MetadataStoreBase<T> {
- private Map<String, Map<Plugin, MetadataValue>> metadataMap = new HashMap<String, Map<Plugin, MetadataValue>>();
+ private Map<String, Map<Plugin, MetadataValue>> metadataMap = new java.util.concurrent.ConcurrentHashMap<String, Map<Plugin, MetadataValue>>(); // Paper
/**
* Adds a metadata value to an object. Each metadata value is owned by a
@@ -46,7 +46,9 @@ public abstract class MetadataStoreBase<T> {
entry = new WeakHashMap<Plugin, MetadataValue>(1);
metadataMap.put(key, entry);
}
- entry.put(owningPlugin, newMetadataValue);
+ synchronized (entry) {
+ entry.put(owningPlugin, newMetadataValue);
+ }
}
/**
@@ -60,10 +62,11 @@ public abstract class MetadataStoreBase<T> {
* @see MetadataStore#getMetadata(Object, String)
*/
@NotNull
- public synchronized List<MetadataValue> getMetadata(@NotNull T subject, @NotNull String metadataKey) {
+ public List<MetadataValue> getMetadata(@NotNull T subject, @NotNull String metadataKey) { // Paper
String key = disambiguate(subject, metadataKey);
- if (metadataMap.containsKey(key)) {
- Collection<MetadataValue> values = metadataMap.get(key).values();
+ Map<Plugin, MetadataValue> entry = metadataMap.get(key);
+ if (entry != null) {
+ Collection<MetadataValue> values = entry.values();
return Collections.unmodifiableList(new ArrayList<MetadataValue>(values));
} else {
return Collections.emptyList();
@@ -78,7 +81,7 @@ public abstract class MetadataStoreBase<T> {
* @param metadataKey the unique metadata key being queried.
* @return the existence of the metadataKey within subject.
*/
- public synchronized boolean hasMetadata(@NotNull T subject, @NotNull String metadataKey) {
+ public boolean hasMetadata(@NotNull T subject, @NotNull String metadataKey) { // Paper
String key = disambiguate(subject, metadataKey);
return metadataMap.containsKey(key);
}
@@ -94,17 +97,18 @@ public abstract class MetadataStoreBase<T> {
* @see MetadataStore#removeMetadata(Object, String,
* org.bukkit.plugin.Plugin)
*/
- public synchronized void removeMetadata(@NotNull T subject, @NotNull String metadataKey, @NotNull Plugin owningPlugin) {
+ public void removeMetadata(@NotNull T subject, @NotNull String metadataKey, @NotNull Plugin owningPlugin) { // Paper
Preconditions.checkArgument(owningPlugin != null, "Plugin cannot be null");
String key = disambiguate(subject, metadataKey);
Map<Plugin, MetadataValue> entry = metadataMap.get(key);
if (entry == null) {
return;
}
-
- entry.remove(owningPlugin);
- if (entry.isEmpty()) {
- metadataMap.remove(key);
+ synchronized (entry) {
+ entry.remove(owningPlugin);
+ if (entry.isEmpty()) {
+ metadataMap.remove(key);
+ }
}
}
@@ -117,7 +121,7 @@ public abstract class MetadataStoreBase<T> {
* @throws IllegalArgumentException If plugin is null
* @see MetadataStore#invalidateAll(org.bukkit.plugin.Plugin)
*/
- public synchronized void invalidateAll(@NotNull Plugin owningPlugin) {
+ public void invalidateAll(@NotNull Plugin owningPlugin) { // Paper
Preconditions.checkArgument(owningPlugin != null, "Plugin cannot be null");
for (Map<Plugin, MetadataValue> values : metadataMap.values()) {
if (values.containsKey(owningPlugin)) {
@@ -1,46 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 16 Jul 2013 21:26:50 -0400
Subject: [PATCH] Add MetadataStoreBase.removeAll(Plugin)
So that on reload, metadata will be cleared
diff --git a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
index a0698522f01619c9d37474f4bd55a17de58fff89..a298baaa7a126515bb2802dd311f8487f3e5533f 100644
--- a/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
+++ b/src/main/java/org/bukkit/metadata/MetadataStoreBase.java
@@ -5,6 +5,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator; // Paper
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -130,6 +131,26 @@ public abstract class MetadataStoreBase<T> {
}
}
+ /**
+ * Removes all metadata in the metadata store that originates from the
+ * given plugin.
+ *
+ * @param owningPlugin the plugin requesting the invalidation.
+ * @throws IllegalArgumentException If plugin is null
+ */
+ public void removeAll(@NotNull Plugin owningPlugin) {
+ Preconditions.checkNotNull(owningPlugin, "Plugin cannot be null");
+ for (Iterator<Map<Plugin, MetadataValue>> iterator = metadataMap.values().iterator(); iterator.hasNext(); ) {
+ Map<Plugin, MetadataValue> values = iterator.next();
+ if (values.containsKey(owningPlugin)) {
+ values.remove(owningPlugin);
+ }
+ if (values.isEmpty()) {
+ iterator.remove();
+ }
+ }
+ }
+
/**
* Creates a unique name for the object receiving metadata by combining
* unique data from the subject with a metadataKey.
@@ -0,0 +1,58 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jedediah Smith <jedediah@silencegreys.com>
Date: Sat, 2 Apr 2016 05:08:36 -0400
Subject: [PATCH] Add PlayerUseUnknownEntityEvent
diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerUseUnknownEntityEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerUseUnknownEntityEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..09cfdf48ead8f03f3497646537292174241b0868
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerUseUnknownEntityEvent.java
@@ -0,0 +1,46 @@
+package com.destroystokyo.paper.event.player;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerEvent;
+import org.bukkit.inventory.EquipmentSlot;
+import org.jetbrains.annotations.NotNull;
+
+public class PlayerUseUnknownEntityEvent extends PlayerEvent {
+
+ private static final HandlerList handlers = new HandlerList();
+ private final int entityId;
+ private final boolean attack;
+ @NotNull private final EquipmentSlot hand;
+
+ public PlayerUseUnknownEntityEvent(@NotNull Player who, int entityId, boolean attack, @NotNull EquipmentSlot hand) {
+ super(who);
+ this.entityId = entityId;
+ this.attack = attack;
+ this.hand = hand;
+ }
+
+ public int getEntityId() {
+ return this.entityId;
+ }
+
+ public boolean isAttack() {
+ return this.attack;
+ }
+
+ @NotNull
+ public EquipmentSlot getHand() {
+ return this.hand;
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
@@ -1,98 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jedediah Smith <jedediah@silencegreys.com>
Date: Sat, 2 Apr 2016 05:08:36 -0400
Subject: [PATCH] Add PlayerUseUnknownEntityEvent
Adds the PlayerUseUnknownEntityEvent to be used by plugins dealing with
virtual entities/entities that are not actually known to the server.
Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerUseUnknownEntityEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerUseUnknownEntityEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7228e248e3d8eb2e9f011dfe2c7dd9f3f48000f
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerUseUnknownEntityEvent.java
@@ -0,0 +1,82 @@
+package com.destroystokyo.paper.event.player;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerEvent;
+import org.bukkit.inventory.EquipmentSlot;
+import org.bukkit.util.Vector;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Represents an event that is called when a player right-clicks an unknown entity.
+ * Useful for plugins dealing with virtual entities (entities that don't actually spawned on the server).
+ * <br>
+ * This event may be called multiple times per interaction with different interaction hands
+ * and with or without the clicked position.
+ */
+public class PlayerUseUnknownEntityEvent extends PlayerEvent {
+
+ private static final HandlerList HANDLERS = new HandlerList();
+ private final int entityId;
+ private final boolean attack;
+ private final @NotNull EquipmentSlot hand;
+ private final @Nullable Vector clickedPosition;
+
+ public PlayerUseUnknownEntityEvent(@NotNull Player who, int entityId, boolean attack, @NotNull EquipmentSlot hand, @Nullable Vector clickedPosition) {
+ super(who);
+ this.entityId = entityId;
+ this.attack = attack;
+ this.hand = hand;
+ this.clickedPosition = clickedPosition;
+ }
+
+ /**
+ * Returns the entity id of the unknown entity that was interacted with.
+ *
+ * @return the entity id of the entity that was interacted with
+ */
+ public int getEntityId() {
+ return this.entityId;
+ }
+
+ /**
+ * Returns whether the interaction was an attack.
+ *
+ * @return true if the player is attacking the entity, false if the player is interacting with the entity
+ */
+ public boolean isAttack() {
+ return this.attack;
+ }
+
+ /**
+ * Returns the hand used to perform this interaction.
+ *
+ * @return the hand used to interact
+ */
+ public @NotNull EquipmentSlot getHand() {
+ return this.hand;
+ }
+
+ /**
+ * Returns the position relative to the entity that was clicked, or null if not available.
+ * See {@link org.bukkit.event.player.PlayerInteractAtEntityEvent} for more details.
+ *
+ * @return the position relative to the entity that was clicked, or null if not available
+ * @see org.bukkit.event.player.PlayerInteractAtEntityEvent
+ */
+ public @Nullable Vector getClickedRelativePosition() {
+ return this.clickedPosition != null ? this.clickedPosition.clone() : null;
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLERS;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return HANDLERS;
+ }
+}
@@ -0,0 +1,290 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kashike <kashike@vq.lc>
Date: Wed, 13 Apr 2016 20:20:18 -0700
Subject: [PATCH] Add handshake event to allow plugins to handle client
handshaking logic themselves
diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..a077962fa786a3291849abfa823c7f0ec4664fce
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java
@@ -0,0 +1,277 @@
+package com.destroystokyo.paper.event.player;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.apache.commons.lang.Validate;
+import org.bukkit.Bukkit;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+import java.util.UUID;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This event is fired during a player handshake.
+ *
+ * <p>If there are no listeners listening to this event, the logic default
+ * to your server platform will be ran.</p>
+ *
+ * <p>WARNING: TAMPERING WITH THIS EVENT CAN BE DANGEROUS</p>
+ */
+public class PlayerHandshakeEvent extends Event implements Cancellable {
+
+ private static final HandlerList HANDLERS = new HandlerList();
+ @NotNull private final String originalHandshake;
+ @NotNull private final String originalSocketAddressHostname;
+ private boolean cancelled;
+ @Nullable private String serverHostname;
+ @Nullable private String socketAddressHostname;
+ @Nullable private UUID uniqueId;
+ @Nullable private String propertiesJson;
+ private boolean failed;
+ private Component failMessage = Component.text("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!", NamedTextColor.YELLOW);
+
+ /**
+ * Creates a new {@link PlayerHandshakeEvent}.
+ *
+ * @param originalHandshake the original handshake string
+ * @param cancelled if this event is enabled
+ *
+ * @deprecated in favour of {@link PlayerHandshakeEvent(String, String, boolean)}
+ */
+ @Deprecated
+ public PlayerHandshakeEvent(@NotNull String originalHandshake, boolean cancelled) {
+ this(originalHandshake, "127.0.0.1", cancelled);
+ }
+
+ /**
+ * Creates a new {@link PlayerHandshakeEvent}.
+ *
+ * @param originalHandshake the original handshake string
+ * @param originalSocketAddressHostname the original socket address hostname
+ * @param cancelled if this event is enabled
+ */
+ public PlayerHandshakeEvent(@NotNull String originalHandshake, @NotNull String originalSocketAddressHostname, boolean cancelled) {
+ super(true);
+ this.originalHandshake = originalHandshake;
+ this.originalSocketAddressHostname = originalSocketAddressHostname;
+ this.cancelled = cancelled;
+ }
+
+ /**
+ * Determines if this event is cancelled.
+ *
+ * <p>When this event is cancelled, custom handshake logic will not
+ * be processed.</p>
+ *
+ * @return {@code true} if this event is cancelled, {@code false} otherwise
+ */
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ /**
+ * Sets if this event is cancelled.
+ *
+ * <p>When this event is cancelled, custom handshake logic will not
+ * be processed.</p>
+ *
+ * @param cancelled {@code true} if this event is cancelled, {@code false} otherwise
+ */
+ @Override
+ public void setCancelled(boolean cancelled) {
+ this.cancelled = cancelled;
+ }
+
+ /**
+ * Gets the original handshake string.
+ *
+ * @return the original handshake string
+ */
+ @NotNull
+ public String getOriginalHandshake() {
+ return this.originalHandshake;
+ }
+
+ /**
+ * Gets the original socket address hostname.
+ *
+ * <p>This does not include the port.</p>
+ * <p>In cases where this event is manually fired and the plugin wasn't updated yet, the default is {@code "127.0.0.1"}.</p>
+ *
+ * @return the original socket address hostname
+ */
+ @NotNull
+ public String getOriginalSocketAddressHostname() {
+ return this.originalSocketAddressHostname;
+ }
+
+ /**
+ * Gets the server hostname string.
+ *
+ * <p>This should not include the port.</p>
+ *
+ * @return the server hostname string
+ */
+ @Nullable
+ public String getServerHostname() {
+ return this.serverHostname;
+ }
+
+ /**
+ * Sets the server hostname string.
+ *
+ * <p>This should not include the port.</p>
+ *
+ * @param serverHostname the server hostname string
+ */
+ public void setServerHostname(@NotNull String serverHostname) {
+ this.serverHostname = serverHostname;
+ }
+
+ /**
+ * Gets the socket address hostname string.
+ *
+ * <p>This should not include the port.</p>
+ *
+ * @return the socket address hostname string
+ */
+ @Nullable
+ public String getSocketAddressHostname() {
+ return this.socketAddressHostname;
+ }
+
+ /**
+ * Sets the socket address hostname string.
+ *
+ * <p>This should not include the port.</p>
+ *
+ * @param socketAddressHostname the socket address hostname string
+ */
+ public void setSocketAddressHostname(@NotNull String socketAddressHostname) {
+ this.socketAddressHostname = socketAddressHostname;
+ }
+
+ /**
+ * Gets the unique id.
+ *
+ * @return the unique id
+ */
+ @Nullable
+ public UUID getUniqueId() {
+ return this.uniqueId;
+ }
+
+ /**
+ * Sets the unique id.
+ *
+ * @param uniqueId the unique id
+ */
+ public void setUniqueId(@NotNull UUID uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ /**
+ * Gets the profile properties.
+ *
+ * <p>This should be a valid JSON string.</p>
+ *
+ * @return the profile properties, as JSON
+ */
+ @Nullable
+ public String getPropertiesJson() {
+ return this.propertiesJson;
+ }
+
+ /**
+ * Determines if authentication failed.
+ *
+ * <p>When {@code true}, the client connecting will be disconnected
+ * with the {@link #getFailMessage() fail message}.</p>
+ *
+ * @return {@code true} if authentication failed, {@code false} otherwise
+ */
+ public boolean isFailed() {
+ return this.failed;
+ }
+
+ /**
+ * Sets if authentication failed and the client should be disconnected.
+ *
+ * <p>When {@code true}, the client connecting will be disconnected
+ * with the {@link #getFailMessage() fail message}.</p>
+ *
+ * @param failed {@code true} if authentication failed, {@code false} otherwise
+ */
+ public void setFailed(boolean failed) {
+ this.failed = failed;
+ }
+
+ /**
+ * Sets the profile properties.
+ *
+ * <p>This should be a valid JSON string.</p>
+ *
+ * @param propertiesJson the profile properties, as JSON
+ */
+ public void setPropertiesJson(@NotNull String propertiesJson) {
+ this.propertiesJson = propertiesJson;
+ }
+
+ /**
+ * Gets the message to display to the client when authentication fails.
+ *
+ * @return the message to display to the client
+ */
+ @NotNull
+ public Component failMessage() {
+ return this.failMessage;
+ }
+
+ /**
+ * Sets the message to display to the client when authentication fails.
+ *
+ * @param failMessage the message to display to the client
+ */
+ public void failMessage(@NotNull Component failMessage) {
+ this.failMessage = failMessage;
+ }
+
+ /**
+ * Gets the message to display to the client when authentication fails.
+ *
+ * @return the message to display to the client
+ * @deprecated use {@link #failMessage()}
+ */
+ @NotNull
+ @Deprecated
+ public String getFailMessage() {
+ return Bukkit.getUnsafe().legacyComponentSerializer().serialize(this.failMessage());
+ }
+
+ /**
+ * Sets the message to display to the client when authentication fails.
+ *
+ * @param failMessage the message to display to the client
+ * @deprecated use {@link #failMessage(Component)}
+ */
+ @Deprecated
+ public void setFailMessage(@NotNull String failMessage) {
+ Validate.notEmpty(failMessage, "fail message cannot be null or empty");
+ this.failMessage(Bukkit.getUnsafe().legacyComponentSerializer().deserialize(failMessage));
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLERS;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return HANDLERS;
+ }
+}
@@ -1,290 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kashike <kashike@vq.lc>
Date: Wed, 13 Apr 2016 20:20:18 -0700
Subject: [PATCH] Add handshake event to allow plugins to handle client
handshaking logic themselves
diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..59ae7bc3a0a2079fe4b3a92d777aca682a58e4e3
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java
@@ -0,0 +1,277 @@
+package com.destroystokyo.paper.event.player;
+
+import com.google.common.base.Preconditions;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.UUID;
+
+/**
+ * This event is fired during a player handshake.
+ *
+ * <p>If there are no listeners listening to this event, the logic default
+ * to your server platform will be ran.</p>
+ *
+ * <p>WARNING: TAMPERING WITH THIS EVENT CAN BE DANGEROUS</p>
+ */
+public class PlayerHandshakeEvent extends Event implements Cancellable {
+
+ private static final HandlerList HANDLERS = new HandlerList();
+ @NotNull private final String originalHandshake;
+ @NotNull private final String originalSocketAddressHostname;
+ private boolean cancelled;
+ @Nullable private String serverHostname;
+ @Nullable private String socketAddressHostname;
+ @Nullable private UUID uniqueId;
+ @Nullable private String propertiesJson;
+ private boolean failed;
+ private Component failMessage = Component.text("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!", NamedTextColor.YELLOW);
+
+ /**
+ * Creates a new {@link PlayerHandshakeEvent}.
+ *
+ * @param originalHandshake the original handshake string
+ * @param cancelled if this event is enabled
+ *
+ * @deprecated in favour of {@link PlayerHandshakeEvent(String, String, boolean)}
+ */
+ @Deprecated
+ public PlayerHandshakeEvent(@NotNull String originalHandshake, boolean cancelled) {
+ this(originalHandshake, "127.0.0.1", cancelled);
+ }
+
+ /**
+ * Creates a new {@link PlayerHandshakeEvent}.
+ *
+ * @param originalHandshake the original handshake string
+ * @param originalSocketAddressHostname the original socket address hostname
+ * @param cancelled if this event is enabled
+ */
+ public PlayerHandshakeEvent(@NotNull String originalHandshake, @NotNull String originalSocketAddressHostname, boolean cancelled) {
+ super(true);
+ this.originalHandshake = originalHandshake;
+ this.originalSocketAddressHostname = originalSocketAddressHostname;
+ this.cancelled = cancelled;
+ }
+
+ /**
+ * Determines if this event is cancelled.
+ *
+ * <p>When this event is cancelled, custom handshake logic will not
+ * be processed.</p>
+ *
+ * @return {@code true} if this event is cancelled, {@code false} otherwise
+ */
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ /**
+ * Sets if this event is cancelled.
+ *
+ * <p>When this event is cancelled, custom handshake logic will not
+ * be processed.</p>
+ *
+ * @param cancelled {@code true} if this event is cancelled, {@code false} otherwise
+ */
+ @Override
+ public void setCancelled(boolean cancelled) {
+ this.cancelled = cancelled;
+ }
+
+ /**
+ * Gets the original handshake string.
+ *
+ * @return the original handshake string
+ */
+ @NotNull
+ public String getOriginalHandshake() {
+ return this.originalHandshake;
+ }
+
+ /**
+ * Gets the original socket address hostname.
+ *
+ * <p>This does not include the port.</p>
+ * <p>In cases where this event is manually fired and the plugin wasn't updated yet, the default is {@code "127.0.0.1"}.</p>
+ *
+ * @return the original socket address hostname
+ */
+ @NotNull
+ public String getOriginalSocketAddressHostname() {
+ return this.originalSocketAddressHostname;
+ }
+
+ /**
+ * Gets the server hostname string.
+ *
+ * <p>This should not include the port.</p>
+ *
+ * @return the server hostname string
+ */
+ @Nullable
+ public String getServerHostname() {
+ return this.serverHostname;
+ }
+
+ /**
+ * Sets the server hostname string.
+ *
+ * <p>This should not include the port.</p>
+ *
+ * @param serverHostname the server hostname string
+ */
+ public void setServerHostname(@NotNull String serverHostname) {
+ this.serverHostname = serverHostname;
+ }
+
+ /**
+ * Gets the socket address hostname string.
+ *
+ * <p>This should not include the port.</p>
+ *
+ * @return the socket address hostname string
+ */
+ @Nullable
+ public String getSocketAddressHostname() {
+ return this.socketAddressHostname;
+ }
+
+ /**
+ * Sets the socket address hostname string.
+ *
+ * <p>This should not include the port.</p>
+ *
+ * @param socketAddressHostname the socket address hostname string
+ */
+ public void setSocketAddressHostname(@NotNull String socketAddressHostname) {
+ this.socketAddressHostname = socketAddressHostname;
+ }
+
+ /**
+ * Gets the unique id.
+ *
+ * @return the unique id
+ */
+ @Nullable
+ public UUID getUniqueId() {
+ return this.uniqueId;
+ }
+
+ /**
+ * Sets the unique id.
+ *
+ * @param uniqueId the unique id
+ */
+ public void setUniqueId(@NotNull UUID uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ /**
+ * Gets the profile properties.
+ *
+ * <p>This should be a valid JSON string.</p>
+ *
+ * @return the profile properties, as JSON
+ */
+ @Nullable
+ public String getPropertiesJson() {
+ return this.propertiesJson;
+ }
+
+ /**
+ * Determines if authentication failed.
+ *
+ * <p>When {@code true}, the client connecting will be disconnected
+ * with the {@link #getFailMessage() fail message}.</p>
+ *
+ * @return {@code true} if authentication failed, {@code false} otherwise
+ */
+ public boolean isFailed() {
+ return this.failed;
+ }
+
+ /**
+ * Sets if authentication failed and the client should be disconnected.
+ *
+ * <p>When {@code true}, the client connecting will be disconnected
+ * with the {@link #getFailMessage() fail message}.</p>
+ *
+ * @param failed {@code true} if authentication failed, {@code false} otherwise
+ */
+ public void setFailed(boolean failed) {
+ this.failed = failed;
+ }
+
+ /**
+ * Sets the profile properties.
+ *
+ * <p>This should be a valid JSON string.</p>
+ *
+ * @param propertiesJson the profile properties, as JSON
+ */
+ public void setPropertiesJson(@NotNull String propertiesJson) {
+ this.propertiesJson = propertiesJson;
+ }
+
+ /**
+ * Gets the message to display to the client when authentication fails.
+ *
+ * @return the message to display to the client
+ */
+ @NotNull
+ public Component failMessage() {
+ return this.failMessage;
+ }
+
+ /**
+ * Sets the message to display to the client when authentication fails.
+ *
+ * @param failMessage the message to display to the client
+ */
+ public void failMessage(@NotNull Component failMessage) {
+ this.failMessage = failMessage;
+ }
+
+ /**
+ * Gets the message to display to the client when authentication fails.
+ *
+ * @return the message to display to the client
+ * @deprecated use {@link #failMessage()}
+ */
+ @NotNull
+ @Deprecated
+ public String getFailMessage() {
+ return LegacyComponentSerializer.legacySection().serialize(this.failMessage());
+ }
+
+ /**
+ * Sets the message to display to the client when authentication fails.
+ *
+ * @param failMessage the message to display to the client
+ * @deprecated use {@link #failMessage(Component)}
+ */
+ @Deprecated
+ public void setFailMessage(@NotNull String failMessage) {
+ Preconditions.checkArgument(failMessage != null && !failMessage.isEmpty(), "fail message cannot be null or empty");
+ this.failMessage(LegacyComponentSerializer.legacySection().deserialize(failMessage));
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLERS;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return HANDLERS;
+ }
+}
+402
View File
@@ -0,0 +1,402 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 1 May 2016 15:19:49 -0400
Subject: [PATCH] LootTable API
Provides API to control what Loot Table an object uses.
Also provides an Event to control if a lootable inventory should
auto replenish for a player.
Provides methods to determine players looted state for an object
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..92d7b853a2ccaae5afa8ac141bead840942944ef
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java
@@ -0,0 +1,17 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.block.Block;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents an Inventory that can generate loot, such as Chests inside of Fortresses and Mineshafts
+ */
+public interface LootableBlockInventory extends LootableInventory {
+
+ /**
+ * Gets the block that is lootable
+ * @return The Block
+ */
+ @NotNull
+ Block getBlock();
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..b387894fe8001edb41ad2ad2b70ebabe065b682e
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java
@@ -0,0 +1,17 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.entity.Entity;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents an Inventory that can generate loot, such as Minecarts inside of Mineshafts
+ */
+public interface LootableEntityInventory extends LootableInventory {
+
+ /**
+ * Gets the entity that is lootable
+ * @return The Entity
+ */
+ @NotNull
+ Entity getEntity();
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..97815eeb231cf0706b34fa47a4f7d1bb786305b4
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java
@@ -0,0 +1,116 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.entity.Player;
+import org.bukkit.loot.Lootable;
+
+import java.util.UUID;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Represents an Inventory that contains a Loot Table associated to it that will
+ * automatically fill on first open.
+ *
+ * A new feature and API is provided to support automatically refreshing the contents
+ * of the inventory based on that Loot Table after a configurable amount of time has passed.
+ *
+ * The behavior of how the Inventory is filled based on the loot table may vary based
+ * on Minecraft versions and the Loot Table feature.
+ */
+public interface LootableInventory extends Lootable {
+
+ /**
+ * Server owners have to enable whether or not an object in a world should refill
+ *
+ * @return If the world this inventory is currently in has Replenishable Lootables enabled
+ */
+ boolean isRefillEnabled();
+
+ /**
+ * Whether or not this object has ever been filled
+ * @return Has ever been filled
+ */
+ boolean hasBeenFilled();
+
+ /**
+ * Has this player ever looted this block
+ * @param player The player to check
+ * @return Whether or not this player has looted this block
+ */
+ default boolean hasPlayerLooted(@NotNull Player player) {
+ return hasPlayerLooted(player.getUniqueId());
+ }
+
+ /**
+ * Has this player ever looted this block
+ * @param player The player to check
+ * @return Whether or not this player has looted this block
+ */
+ boolean hasPlayerLooted(@NotNull UUID player);
+
+ /**
+ * Gets the timestamp, in milliseconds, of when the player last looted this object
+ *
+ * @param player The player to check
+ * @return Timestamp last looted, or null if player has not looted this object
+ */
+ @Nullable
+ default Long getLastLooted(@NotNull Player player) {
+ return getLastLooted(player.getUniqueId());
+ }
+
+ /**
+ * Gets the timestamp, in milliseconds, of when the player last looted this object
+ *
+ * @param player The player to check
+ * @return Timestamp last looted, or null if player has not looted this object
+ */
+ @Nullable
+ Long getLastLooted(@NotNull UUID player);
+
+ /**
+ * Change the state of whether or not a player has looted this block
+ * @param player The player to change state for
+ * @param looted true to add player to looted list, false to remove
+ * @return The previous state of whether the player had looted this or not
+ */
+ default boolean setHasPlayerLooted(@NotNull Player player, boolean looted) {
+ return setHasPlayerLooted(player.getUniqueId(), looted);
+ }
+
+ /**
+ * Change the state of whether or not a player has looted this block
+ * @param player The player to change state for
+ * @param looted true to add player to looted list, false to remove
+ * @return The previous state of whether the player had looted this or not
+ */
+ boolean setHasPlayerLooted(@NotNull UUID player, boolean looted);
+
+ /**
+ * Returns Whether or not this object has been filled and now has a pending refill
+ * @return Has pending refill
+ */
+ boolean hasPendingRefill();
+
+ /**
+ * Gets the timestamp in milliseconds that the Lootable object was last refilled
+ *
+ * @return -1 if it was never refilled, or timestamp in milliseconds
+ */
+ long getLastFilled();
+
+ /**
+ * Gets the timestamp in milliseconds that the Lootable object will refill
+ *
+ * @return -1 if it is not scheduled for refill, or timestamp in milliseconds
+ */
+ long getNextRefill();
+
+ /**
+ * Sets the timestamp in milliseconds of the next refill for this object
+ *
+ * @param refillAt timestamp in milliseconds. -1 to clear next refill
+ * @return The previous scheduled time to refill, or -1 if was not scheduled
+ */
+ long setNextRefill(long refillAt);
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java b/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd184f13f5e8ee5cf829fff4f44696e1f760430b
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java
@@ -0,0 +1,45 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerEvent;
+import org.jetbrains.annotations.NotNull;
+
+public class LootableInventoryReplenishEvent extends PlayerEvent implements Cancellable {
+ @NotNull private final LootableInventory inventory;
+
+ public LootableInventoryReplenishEvent(@NotNull Player player, @NotNull LootableInventory inventory) {
+ super(player);
+ this.inventory = inventory;
+ }
+
+ @NotNull
+ public LootableInventory getInventory() {
+ return inventory;
+ }
+
+ private static final HandlerList handlers = new HandlerList();
+
+ @NotNull
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ private boolean cancelled = false;
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ cancelled = cancel;
+ }
+}
diff --git a/src/main/java/org/bukkit/block/Chest.java b/src/main/java/org/bukkit/block/Chest.java
index b451191312e4fb19f2131c2d0a0c0337953f6c7c..db6affbc78106b2d93b41953b624a0bca0ca1d72 100644
--- a/src/main/java/org/bukkit/block/Chest.java
+++ b/src/main/java/org/bukkit/block/Chest.java
@@ -1,5 +1,7 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory; // Paper
+import org.bukkit.Nameable; // Paper
import org.bukkit.inventory.Inventory;
import org.bukkit.loot.Lootable;
import org.jetbrains.annotations.NotNull;
@@ -7,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents a captured state of a chest.
*/
-public interface Chest extends Container, Lootable, Lidded {
+public interface Chest extends Container, LootableBlockInventory, Lidded { // Paper
/**
* Gets the inventory of the chest block represented by this block state.
diff --git a/src/main/java/org/bukkit/block/Dispenser.java b/src/main/java/org/bukkit/block/Dispenser.java
index 74cd194c9a98245dc52e7e352d7d6c046e1e5cf3..07af1a3f011d4b96275f919d302ac367198e923e 100644
--- a/src/main/java/org/bukkit/block/Dispenser.java
+++ b/src/main/java/org/bukkit/block/Dispenser.java
@@ -1,5 +1,6 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.Nameable;
import org.bukkit.loot.Lootable;
import org.bukkit.projectiles.BlockProjectileSource;
@@ -8,7 +9,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a captured state of a dispenser.
*/
-public interface Dispenser extends Container, Nameable, Lootable {
+public interface Dispenser extends Container, Nameable, LootableBlockInventory { // Paper
/**
* Gets the BlockProjectileSource object for the dispenser.
diff --git a/src/main/java/org/bukkit/block/Dropper.java b/src/main/java/org/bukkit/block/Dropper.java
index 424392fb5ed4628199b0e73689522aa3c90740cb..c76202321e29ad67597ca3017eb8d9baf6787383 100644
--- a/src/main/java/org/bukkit/block/Dropper.java
+++ b/src/main/java/org/bukkit/block/Dropper.java
@@ -1,11 +1,12 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.loot.Lootable;
/**
* Represents a captured state of a dropper.
*/
-public interface Dropper extends Container, Lootable {
+public interface Dropper extends Container, LootableBlockInventory { // Paper
/**
* Tries to drop a randomly selected item from the dropper's inventory,
diff --git a/src/main/java/org/bukkit/block/Hopper.java b/src/main/java/org/bukkit/block/Hopper.java
index 58e493099810fb8d4705ecd49b4a5e1e1949b87b..7ade312f180b7e30871d3a3240c76325cc369c26 100644
--- a/src/main/java/org/bukkit/block/Hopper.java
+++ b/src/main/java/org/bukkit/block/Hopper.java
@@ -1,8 +1,9 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.loot.Lootable;
/**
* Represents a captured state of a hopper.
*/
-public interface Hopper extends Container, Lootable { }
+public interface Hopper extends Container, LootableBlockInventory { } // Paper
diff --git a/src/main/java/org/bukkit/block/ShulkerBox.java b/src/main/java/org/bukkit/block/ShulkerBox.java
index 387b2892886e1ccb2bd928e5111fb9bd41d777ab..5dc5318b0a451937228a8a059dfec1cd9de389a6 100644
--- a/src/main/java/org/bukkit/block/ShulkerBox.java
+++ b/src/main/java/org/bukkit/block/ShulkerBox.java
@@ -1,5 +1,6 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.DyeColor;
import org.bukkit.loot.Lootable;
import org.jetbrains.annotations.Nullable;
@@ -7,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a captured state of a ShulkerBox.
*/
-public interface ShulkerBox extends Container, Lootable, Lidded {
+public interface ShulkerBox extends Container, LootableBlockInventory, Lidded { // Paper
/**
* Get the {@link DyeColor} corresponding to this ShulkerBox
diff --git a/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java b/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
index 937b99f8734d71b2ad33af142afbc251b81d9745..db69687a7ad4b18d17ab1677cae5d8dd4dcd3678 100644
--- a/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
+++ b/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
@@ -1,5 +1,6 @@
package org.bukkit.entity.minecart;
+import com.destroystokyo.paper.loottable.LootableEntityInventory;
import org.bukkit.entity.Minecart;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.loot.Lootable;
@@ -7,7 +8,7 @@ import org.bukkit.loot.Lootable;
/**
* Represents a Minecart with a Hopper inside it
*/
-public interface HopperMinecart extends Minecart, InventoryHolder, Lootable {
+public interface HopperMinecart extends Minecart, InventoryHolder, LootableEntityInventory {
/**
* Checks whether or not this Minecart will pick up
diff --git a/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java b/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
index 9ea403e6fd8e960d017660e0aec118abeda2c42b..238d118f7788b13cd86b7e9ea3a0fc38e2e09715 100644
--- a/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
+++ b/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
@@ -1,5 +1,6 @@
package org.bukkit.entity.minecart;
+import com.destroystokyo.paper.loottable.LootableEntityInventory;
import org.bukkit.entity.Minecart;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.loot.Lootable;
@@ -9,5 +10,5 @@ import org.bukkit.loot.Lootable;
* minecarts} have their own inventory that can be accessed using methods
* from the {@link InventoryHolder} interface.
*/
-public interface StorageMinecart extends Minecart, InventoryHolder, Lootable {
+public interface StorageMinecart extends Minecart, InventoryHolder, LootableEntityInventory { // Paper
}
diff --git a/src/main/java/org/bukkit/loot/Lootable.java b/src/main/java/org/bukkit/loot/Lootable.java
index 24a3d989db3bc67e7afe8459a3d4bb132f448ea7..901db852498e0658c79a57582508dab29bf0a798 100644
--- a/src/main/java/org/bukkit/loot/Lootable.java
+++ b/src/main/java/org/bukkit/loot/Lootable.java
@@ -36,6 +36,34 @@ public interface Lootable {
@Nullable
LootTable getLootTable();
+ // Paper start
+ /**
+ * Set the loot table and seed for a container or entity at the same time.
+ *
+ * @param table the Loot Table this {@link org.bukkit.block.Container} or {@link org.bukkit.entity.Mob} will have.
+ * @param seed the seed to used to generate loot. Default is 0.
+ */
+ default void setLootTable(@Nullable LootTable table, long seed) {
+ setLootTable(table);
+ setSeed(seed);
+ }
+
+ /**
+ * Returns whether or not this object has a Loot Table
+ * @return Has a loot table
+ */
+ default boolean hasLootTable() {
+ return getLootTable() != null;
+ }
+
+ /**
+ * Clears the associated Loot Table to this object
+ */
+ default void clearLootTable() {
+ setLootTable(null);
+ }
+ // Paper end
+
/**
* Set the seed used when this Loot Table generates loot.
*
+123
View File
@@ -0,0 +1,123 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: AlphaBlend <whizkid3000@hotmail.com>
Date: Sun, 16 Oct 2016 23:19:34 -0700
Subject: [PATCH] Add EntityZapEvent
diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EntityZapEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EntityZapEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b725a489008d333630af166d2be5fc48168a6b9
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/entity/EntityZapEvent.java
@@ -0,0 +1,65 @@
+package com.destroystokyo.paper.event.entity;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.LightningStrike;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.entity.EntityEvent;
+import org.bukkit.event.entity.EntityTransformEvent;
+
+import java.util.Collections;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Fired when lightning strikes an entity
+ */
+public class EntityZapEvent extends EntityTransformEvent implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+ private boolean cancelled;
+ @NotNull private final LightningStrike bolt;
+
+ public EntityZapEvent(@NotNull final Entity entity, @NotNull final LightningStrike bolt, @NotNull final Entity replacementEntity) {
+ super(entity, Collections.singletonList(replacementEntity), TransformReason.LIGHTNING);
+ Validate.notNull(bolt);
+ Validate.notNull(replacementEntity);
+ this.bolt = bolt;
+ }
+
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ public void setCancelled(boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ /**
+ * Gets the lightning bolt that is striking the entity.
+ * @return The lightning bolt responsible for this event
+ */
+ @NotNull
+ public LightningStrike getBolt() {
+ return bolt;
+ }
+
+ /**
+ * Gets the entity that will replace the struck entity.
+ * @return The entity that will replace the struck entity
+ */
+ @NotNull
+ public Entity getReplacementEntity() {
+ return getTransformedEntity();
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/src/main/java/org/bukkit/event/entity/PigZapEvent.java b/src/main/java/org/bukkit/event/entity/PigZapEvent.java
index 0e0ed93b568fd2c0d8f6e359c31dc29cb0fa71c2..d3949edfc736b3d67a627ef378748b374769e183 100644
--- a/src/main/java/org/bukkit/event/entity/PigZapEvent.java
+++ b/src/main/java/org/bukkit/event/entity/PigZapEvent.java
@@ -2,6 +2,7 @@ package org.bukkit.event.entity;
import java.util.Collections;
import org.bukkit.entity.Entity;
+import com.destroystokyo.paper.event.entity.EntityZapEvent;
import org.bukkit.entity.LightningStrike;
import org.bukkit.entity.Pig;
import org.bukkit.entity.PigZombie;
@@ -12,14 +13,14 @@ import org.jetbrains.annotations.NotNull;
/**
* Stores data for pigs being zapped
*/
-public class PigZapEvent extends EntityTransformEvent implements Cancellable {
- private static final HandlerList handlers = new HandlerList();
+public class PigZapEvent extends EntityZapEvent implements Cancellable {
+ //private static final HandlerList handlers = new HandlerList();
private boolean canceled;
private final PigZombie pigzombie;
private final LightningStrike bolt;
public PigZapEvent(@NotNull final Pig pig, @NotNull final LightningStrike bolt, @NotNull final PigZombie pigzombie) {
- super(pig, Collections.singletonList((Entity) pigzombie), TransformReason.LIGHTNING);
+ super(pig, bolt, pigzombie);
this.bolt = bolt;
this.pigzombie = pigzombie;
}
@@ -63,6 +64,8 @@ public class PigZapEvent extends EntityTransformEvent implements Cancellable {
return pigzombie;
}
+ // Paper start
+ /*
@NotNull
@Override
public HandlerList getHandlers() {
@@ -73,4 +76,6 @@ public class PigZapEvent extends EntityTransformEvent implements Cancellable {
public static HandlerList getHandlerList() {
return handlers;
}
+ */
+ // Paper end
}
-422
View File
@@ -1,422 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 1 May 2016 15:19:49 -0400
Subject: [PATCH] LootTable API
Provides API to control what Loot Table an object uses.
Also provides an Event to control if a lootable inventory should
auto replenish for a player.
Provides methods to determine players looted state for an object
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..92d7b853a2ccaae5afa8ac141bead840942944ef
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java
@@ -0,0 +1,17 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.block.Block;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents an Inventory that can generate loot, such as Chests inside of Fortresses and Mineshafts
+ */
+public interface LootableBlockInventory extends LootableInventory {
+
+ /**
+ * Gets the block that is lootable
+ * @return The Block
+ */
+ @NotNull
+ Block getBlock();
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..b387894fe8001edb41ad2ad2b70ebabe065b682e
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java
@@ -0,0 +1,17 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.entity.Entity;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents an Inventory that can generate loot, such as Minecarts inside of Mineshafts
+ */
+public interface LootableEntityInventory extends LootableInventory {
+
+ /**
+ * Gets the entity that is lootable
+ * @return The Entity
+ */
+ @NotNull
+ Entity getEntity();
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b01a50b6e18856f4c9e28340a7a111cae646a0a
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java
@@ -0,0 +1,125 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.entity.Player;
+import org.bukkit.loot.Lootable;
+
+import java.util.UUID;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Represents an Inventory that contains a Loot Table associated to it that will
+ * automatically fill on first open.
+ *
+ * A new feature and API is provided to support automatically refreshing the contents
+ * of the inventory based on that Loot Table after a configurable amount of time has passed.
+ *
+ * The behavior of how the Inventory is filled based on the loot table may vary based
+ * on Minecraft versions and the Loot Table feature.
+ */
+public interface LootableInventory extends Lootable {
+
+ /**
+ * Server owners have to enable whether or not an object in a world should refill
+ *
+ * @return If the world this inventory is currently in has Replenishable Lootables enabled
+ */
+ boolean isRefillEnabled();
+
+ /**
+ * Whether or not this object has ever been filled
+ * @return Has ever been filled
+ */
+ boolean hasBeenFilled();
+
+ /**
+ * Has this player ever looted this block
+ * @param player The player to check
+ * @return Whether or not this player has looted this block
+ */
+ default boolean hasPlayerLooted(@NotNull Player player) {
+ return hasPlayerLooted(player.getUniqueId());
+ }
+
+ /**
+ * Checks if this player can loot this block. Takes into account the "restrict player reloot" settings
+ *
+ * @param player the player to check
+ *
+ * @return Whether this player can loot this block
+ */
+ boolean canPlayerLoot(@NotNull UUID player);
+
+ /**
+ * Has this player ever looted this block
+ * @param player The player to check
+ * @return Whether or not this player has looted this block
+ */
+ boolean hasPlayerLooted(@NotNull UUID player);
+
+ /**
+ * Gets the timestamp, in milliseconds, of when the player last looted this object
+ *
+ * @param player The player to check
+ * @return Timestamp last looted, or null if player has not looted this object
+ */
+ @Nullable
+ default Long getLastLooted(@NotNull Player player) {
+ return getLastLooted(player.getUniqueId());
+ }
+
+ /**
+ * Gets the timestamp, in milliseconds, of when the player last looted this object
+ *
+ * @param player The player to check
+ * @return Timestamp last looted, or null if player has not looted this object
+ */
+ @Nullable
+ Long getLastLooted(@NotNull UUID player);
+
+ /**
+ * Change the state of whether or not a player has looted this block
+ * @param player The player to change state for
+ * @param looted true to add player to looted list, false to remove
+ * @return The previous state of whether the player had looted this or not
+ */
+ default boolean setHasPlayerLooted(@NotNull Player player, boolean looted) {
+ return setHasPlayerLooted(player.getUniqueId(), looted);
+ }
+
+ /**
+ * Change the state of whether or not a player has looted this block
+ * @param player The player to change state for
+ * @param looted true to add player to looted list, false to remove
+ * @return The previous state of whether the player had looted this or not
+ */
+ boolean setHasPlayerLooted(@NotNull UUID player, boolean looted);
+
+ /**
+ * Returns Whether or not this object has been filled and now has a pending refill
+ * @return Has pending refill
+ */
+ boolean hasPendingRefill();
+
+ /**
+ * Gets the timestamp in milliseconds that the Lootable object was last refilled
+ *
+ * @return -1 if it was never refilled, or timestamp in milliseconds
+ */
+ long getLastFilled();
+
+ /**
+ * Gets the timestamp in milliseconds that the Lootable object will refill
+ *
+ * @return -1 if it is not scheduled for refill, or timestamp in milliseconds
+ */
+ long getNextRefill();
+
+ /**
+ * Sets the timestamp in milliseconds of the next refill for this object
+ *
+ * @param refillAt timestamp in milliseconds. -1 to clear next refill
+ * @return The previous scheduled time to refill, or -1 if was not scheduled
+ */
+ long setNextRefill(long refillAt);
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java b/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd184f13f5e8ee5cf829fff4f44696e1f760430b
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableInventoryReplenishEvent.java
@@ -0,0 +1,45 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerEvent;
+import org.jetbrains.annotations.NotNull;
+
+public class LootableInventoryReplenishEvent extends PlayerEvent implements Cancellable {
+ @NotNull private final LootableInventory inventory;
+
+ public LootableInventoryReplenishEvent(@NotNull Player player, @NotNull LootableInventory inventory) {
+ super(player);
+ this.inventory = inventory;
+ }
+
+ @NotNull
+ public LootableInventory getInventory() {
+ return inventory;
+ }
+
+ private static final HandlerList handlers = new HandlerList();
+
+ @NotNull
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ private boolean cancelled = false;
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ cancelled = cancel;
+ }
+}
diff --git a/src/main/java/org/bukkit/block/Chest.java b/src/main/java/org/bukkit/block/Chest.java
index b451191312e4fb19f2131c2d0a0c0337953f6c7c..db6affbc78106b2d93b41953b624a0bca0ca1d72 100644
--- a/src/main/java/org/bukkit/block/Chest.java
+++ b/src/main/java/org/bukkit/block/Chest.java
@@ -1,5 +1,7 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory; // Paper
+import org.bukkit.Nameable; // Paper
import org.bukkit.inventory.Inventory;
import org.bukkit.loot.Lootable;
import org.jetbrains.annotations.NotNull;
@@ -7,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents a captured state of a chest.
*/
-public interface Chest extends Container, Lootable, Lidded {
+public interface Chest extends Container, LootableBlockInventory, Lidded { // Paper
/**
* Gets the inventory of the chest block represented by this block state.
diff --git a/src/main/java/org/bukkit/block/Dispenser.java b/src/main/java/org/bukkit/block/Dispenser.java
index 74cd194c9a98245dc52e7e352d7d6c046e1e5cf3..07af1a3f011d4b96275f919d302ac367198e923e 100644
--- a/src/main/java/org/bukkit/block/Dispenser.java
+++ b/src/main/java/org/bukkit/block/Dispenser.java
@@ -1,5 +1,6 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.Nameable;
import org.bukkit.loot.Lootable;
import org.bukkit.projectiles.BlockProjectileSource;
@@ -8,7 +9,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a captured state of a dispenser.
*/
-public interface Dispenser extends Container, Nameable, Lootable {
+public interface Dispenser extends Container, Nameable, LootableBlockInventory { // Paper
/**
* Gets the BlockProjectileSource object for the dispenser.
diff --git a/src/main/java/org/bukkit/block/Dropper.java b/src/main/java/org/bukkit/block/Dropper.java
index 424392fb5ed4628199b0e73689522aa3c90740cb..c76202321e29ad67597ca3017eb8d9baf6787383 100644
--- a/src/main/java/org/bukkit/block/Dropper.java
+++ b/src/main/java/org/bukkit/block/Dropper.java
@@ -1,11 +1,12 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.loot.Lootable;
/**
* Represents a captured state of a dropper.
*/
-public interface Dropper extends Container, Lootable {
+public interface Dropper extends Container, LootableBlockInventory { // Paper
/**
* Tries to drop a randomly selected item from the dropper's inventory,
diff --git a/src/main/java/org/bukkit/block/Hopper.java b/src/main/java/org/bukkit/block/Hopper.java
index 58e493099810fb8d4705ecd49b4a5e1e1949b87b..7ade312f180b7e30871d3a3240c76325cc369c26 100644
--- a/src/main/java/org/bukkit/block/Hopper.java
+++ b/src/main/java/org/bukkit/block/Hopper.java
@@ -1,8 +1,9 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.loot.Lootable;
/**
* Represents a captured state of a hopper.
*/
-public interface Hopper extends Container, Lootable { }
+public interface Hopper extends Container, LootableBlockInventory { } // Paper
diff --git a/src/main/java/org/bukkit/block/ShulkerBox.java b/src/main/java/org/bukkit/block/ShulkerBox.java
index 387b2892886e1ccb2bd928e5111fb9bd41d777ab..5dc5318b0a451937228a8a059dfec1cd9de389a6 100644
--- a/src/main/java/org/bukkit/block/ShulkerBox.java
+++ b/src/main/java/org/bukkit/block/ShulkerBox.java
@@ -1,5 +1,6 @@
package org.bukkit.block;
+import com.destroystokyo.paper.loottable.LootableBlockInventory;
import org.bukkit.DyeColor;
import org.bukkit.loot.Lootable;
import org.jetbrains.annotations.Nullable;
@@ -7,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a captured state of a ShulkerBox.
*/
-public interface ShulkerBox extends Container, Lootable, Lidded {
+public interface ShulkerBox extends Container, LootableBlockInventory, Lidded { // Paper
/**
* Get the {@link DyeColor} corresponding to this ShulkerBox
diff --git a/src/main/java/org/bukkit/entity/ChestBoat.java b/src/main/java/org/bukkit/entity/ChestBoat.java
index 5b5c3be107fdaa6c55ceb1bca2c223ebc6ab7f43..4ebe1033c55dbd58d0794809435c935236fabcc2 100644
--- a/src/main/java/org/bukkit/entity/ChestBoat.java
+++ b/src/main/java/org/bukkit/entity/ChestBoat.java
@@ -6,5 +6,5 @@ import org.bukkit.loot.Lootable;
/**
* A {@link Boat} with a chest.
*/
-public interface ChestBoat extends Boat, InventoryHolder, Lootable {
+public interface ChestBoat extends Boat, InventoryHolder, com.destroystokyo.paper.loottable.LootableEntityInventory { // Paper
}
diff --git a/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java b/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
index 937b99f8734d71b2ad33af142afbc251b81d9745..db69687a7ad4b18d17ab1677cae5d8dd4dcd3678 100644
--- a/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
+++ b/src/main/java/org/bukkit/entity/minecart/HopperMinecart.java
@@ -1,5 +1,6 @@
package org.bukkit.entity.minecart;
+import com.destroystokyo.paper.loottable.LootableEntityInventory;
import org.bukkit.entity.Minecart;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.loot.Lootable;
@@ -7,7 +8,7 @@ import org.bukkit.loot.Lootable;
/**
* Represents a Minecart with a Hopper inside it
*/
-public interface HopperMinecart extends Minecart, InventoryHolder, Lootable {
+public interface HopperMinecart extends Minecart, InventoryHolder, LootableEntityInventory {
/**
* Checks whether or not this Minecart will pick up
diff --git a/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java b/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
index 9ea403e6fd8e960d017660e0aec118abeda2c42b..238d118f7788b13cd86b7e9ea3a0fc38e2e09715 100644
--- a/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
+++ b/src/main/java/org/bukkit/entity/minecart/StorageMinecart.java
@@ -1,5 +1,6 @@
package org.bukkit.entity.minecart;
+import com.destroystokyo.paper.loottable.LootableEntityInventory;
import org.bukkit.entity.Minecart;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.loot.Lootable;
@@ -9,5 +10,5 @@ import org.bukkit.loot.Lootable;
* minecarts} have their own inventory that can be accessed using methods
* from the {@link InventoryHolder} interface.
*/
-public interface StorageMinecart extends Minecart, InventoryHolder, Lootable {
+public interface StorageMinecart extends Minecart, InventoryHolder, LootableEntityInventory { // Paper
}
diff --git a/src/main/java/org/bukkit/loot/Lootable.java b/src/main/java/org/bukkit/loot/Lootable.java
index 24a3d989db3bc67e7afe8459a3d4bb132f448ea7..901db852498e0658c79a57582508dab29bf0a798 100644
--- a/src/main/java/org/bukkit/loot/Lootable.java
+++ b/src/main/java/org/bukkit/loot/Lootable.java
@@ -36,6 +36,34 @@ public interface Lootable {
@Nullable
LootTable getLootTable();
+ // Paper start
+ /**
+ * Set the loot table and seed for a container or entity at the same time.
+ *
+ * @param table the Loot Table this {@link org.bukkit.block.Container} or {@link org.bukkit.entity.Mob} will have.
+ * @param seed the seed to used to generate loot. Default is 0.
+ */
+ default void setLootTable(@Nullable LootTable table, long seed) {
+ setLootTable(table);
+ setSeed(seed);
+ }
+
+ /**
+ * Returns whether or not this object has a Loot Table
+ * @return Has a loot table
+ */
+ default boolean hasLootTable() {
+ return getLootTable() != null;
+ }
+
+ /**
+ * Clears the associated Loot Table to this object
+ */
+ default void clearLootTable() {
+ setLootTable(null);
+ }
+ // Paper end
+
/**
* Set the seed used when this Loot Table generates loot.
*
-122
View File
@@ -1,122 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: AlphaBlend <whizkid3000@hotmail.com>
Date: Sun, 16 Oct 2016 23:19:34 -0700
Subject: [PATCH] Add EntityZapEvent
diff --git a/src/main/java/com/destroystokyo/paper/event/entity/EntityZapEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/EntityZapEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c5fa066c9e5e1b1a06df30842a4c7acc4742df8
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/entity/EntityZapEvent.java
@@ -0,0 +1,64 @@
+package com.destroystokyo.paper.event.entity;
+
+import com.google.common.base.Preconditions;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.LightningStrike;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.entity.EntityTransformEvent;
+
+import java.util.Collections;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Fired when lightning strikes an entity
+ */
+public class EntityZapEvent extends EntityTransformEvent implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+ private boolean cancelled;
+ @NotNull private final LightningStrike bolt;
+
+ public EntityZapEvent(@NotNull final Entity entity, @NotNull final LightningStrike bolt, @NotNull final Entity replacementEntity) {
+ super(entity, Collections.singletonList(replacementEntity), TransformReason.LIGHTNING);
+ Preconditions.checkNotNull(bolt);
+ Preconditions.checkNotNull(replacementEntity);
+ this.bolt = bolt;
+ }
+
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ public void setCancelled(boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ /**
+ * Gets the lightning bolt that is striking the entity.
+ * @return The lightning bolt responsible for this event
+ */
+ @NotNull
+ public LightningStrike getBolt() {
+ return bolt;
+ }
+
+ /**
+ * Gets the entity that will replace the struck entity.
+ * @return The entity that will replace the struck entity
+ */
+ @NotNull
+ public Entity getReplacementEntity() {
+ return getTransformedEntity();
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/src/main/java/org/bukkit/event/entity/PigZapEvent.java b/src/main/java/org/bukkit/event/entity/PigZapEvent.java
index 0e0ed93b568fd2c0d8f6e359c31dc29cb0fa71c2..d3949edfc736b3d67a627ef378748b374769e183 100644
--- a/src/main/java/org/bukkit/event/entity/PigZapEvent.java
+++ b/src/main/java/org/bukkit/event/entity/PigZapEvent.java
@@ -2,6 +2,7 @@ package org.bukkit.event.entity;
import java.util.Collections;
import org.bukkit.entity.Entity;
+import com.destroystokyo.paper.event.entity.EntityZapEvent;
import org.bukkit.entity.LightningStrike;
import org.bukkit.entity.Pig;
import org.bukkit.entity.PigZombie;
@@ -12,14 +13,14 @@ import org.jetbrains.annotations.NotNull;
/**
* Stores data for pigs being zapped
*/
-public class PigZapEvent extends EntityTransformEvent implements Cancellable {
- private static final HandlerList handlers = new HandlerList();
+public class PigZapEvent extends EntityZapEvent implements Cancellable {
+ //private static final HandlerList handlers = new HandlerList();
private boolean canceled;
private final PigZombie pigzombie;
private final LightningStrike bolt;
public PigZapEvent(@NotNull final Pig pig, @NotNull final LightningStrike bolt, @NotNull final PigZombie pigzombie) {
- super(pig, Collections.singletonList((Entity) pigzombie), TransformReason.LIGHTNING);
+ super(pig, bolt, pigzombie);
this.bolt = bolt;
this.pigzombie = pigzombie;
}
@@ -63,6 +64,8 @@ public class PigZapEvent extends EntityTransformEvent implements Cancellable {
return pigzombie;
}
+ // Paper start
+ /*
@NotNull
@Override
public HandlerList getHandlers() {
@@ -73,4 +76,6 @@ public class PigZapEvent extends EntityTransformEvent implements Cancellable {
public static HandlerList getHandlerList() {
return handlers;
}
+ */
+ // Paper end
}
File renamed without changes.
Loaded 100 of 2619 files, more files were not shown because too many files have changed in this diff. Show more