Compare commits

...
Author SHA1 Message Date
Jake Potrebic 17fbe152a6 Refactor early config loading
Now its still part of GlobalConfiguration which means
settings can live in their natural places instead of
a separate section.
2026-10-25 16:45:52 -07:00
Nassim Jahnke 8ac3459533 Add back commands.yml 2026-10-25 08:40:15 +01:00
Nassim Jahnke b72728d717 Copy in Geyser yaml loader code 2026-10-25 08:25:58 +01:00
Nassim JahnkeandJake Potrebic df17d86a37 Merge Spigot/Bukkit configs into Paper configs
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
2026-10-24 16:31:53 +02:00
Jason Penilla 8c0b8396d6 [ci skip] Trim agent instructions (#14174) 2026-10-20 15:06:56 +02:00
bc68acd741 Add API Player.canCriticalAttack(Entity) (#13915)
Co-authored-by: Pedro <3602279+Doc94@users.noreply.github.com>
Co-authored-by: Strokkur24 <strokkur.24@gmail.com>
2026-10-20 09:12:46 -03:00
e13b4b3d41 Add Roll EnchantmentOffer API (#12345)
Co-authored-by: Bjarne Koll <git@lynxplay.dev>
Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
Co-authored-by: Doc <git@mrdoc.dev>
2026-10-19 11:16:46 -03:00
David a90108f43c Prevent negative max stack sizes in inventories (#14407) 2026-10-17 22:50:13 +02:00
Jason Penilla dd585fda4d Sync latest Moonrise changes (#14408) 2026-10-17 13:18:35 -07:00
stonar96 1b443e0ac8 Explicitly add remaining anti-xray preset values
Shouldn't be a change in behavior given assumed palette contents, but is clearer
2026-10-17 20:18:11 +02:00
Pedro 29aeb6081b Add Allies and additional rule API (#13853) 2026-10-17 11:14:53 -03:00
Javier Flores 3bfa02fd65 Fix death with zero health in non-ticking Armorstands (#14332) 2026-10-17 10:51:55 -03:00
tofik.07andKevin Raneri 09ee222482 Add the ability to throttle inactive goal selector ticking for mobs (#12781)
Co-authored-by: Kevin Raneri <kevin.raneri@gmail.com>
2026-10-16 16:26:16 +02:00
James Peters 22c663d327 Add PrepareHopperTransferEvent (#14368) 2026-10-16 16:04:08 +02:00
atii3343 4091869eed Fix repeated cookie requests for the same key (#14245) 2026-10-16 15:57:30 +02:00
TomandDoc db93f621b3 Add AttachableDirectional Interface (#12932)
Co-authored-by: Doc <git@mrdoc.dev>
2026-10-16 10:36:54 -03:00
amy 7ec11b289e Fix unix domain socket support (#13731) 2026-10-16 09:45:33 -03:00
ZapolyarnyandDoc 683e60c092 Fix datapack GUIs relying on synchronous container opening (#14084)
Co-authored-by: Doc <git@mrdoc.dev>
2026-10-16 09:06:00 -03:00
Bjarne Koll 39887673fb Move cook speed multiplier application to getTotalCookTime (#14401) 2026-10-15 22:41:12 +01:00
RVSkeLe dba08d2f25 Avoid startup failure when UnixSystem UID lookup fails in JDK 27 (#14395) 2026-10-15 16:30:06 +00:00
Bjarne Koll be4b895635 Use vanilla recipe order for recipe matching (#14389) 2026-10-15 17:16:28 +01:00
Warrior b69f87e5ac [ci skip] Remove nullable 'to' ctor param in PlayerMoveEvent and inheritors (#14398) 2026-10-15 17:14:37 +01:00
Nassim Jahnke f68a55a896 Fix duplicate palette entry handling in anti xray
Fixes #13834
2026-10-15 16:03:39 +02:00
JeremyandDoc a0ded751dc Add event for Entity deciding whether an item belongs in a Container (#13811)
Co-authored-by: Doc <git@mrdoc.dev>
2026-10-14 20:12:14 -03:00
Bjarne Koll a48059a2d4 Correctly serialise decorated pod decorations (#14388) 2026-10-14 22:17:00 +02:00
Bjarne Koll 06b855f0d3 Catch async potion brewer reset mixes
No longer allowed off thread as this syncs recipe
registry data to players.
2026-10-14 21:05:03 +01:00
WarriorandBjarne Koll 56cab99fbf Mitigate jline shutdown race (#14382)
Co-authored-by: Bjarne Koll <git@lynxplay.dev>
2026-10-14 20:47:06 +01:00
Warrior c927bd34ab [ci skip] Remove advice on not using entity scheduler if not targeting Folia (#14384) 2026-10-14 18:48:16 +00:00
Jason Penilla b5eb5e0bfd [ci skip] Add manual dispatch trigger
When a build is partially published (i.e. to Fill but not to Maven), a retry of the same build may reject indefinitely. A manual trigger allows resolving this without an empty commit.

In an observed case, Fill successfully published but returned a 504. This specific case could probably be handled with idempotent publish and/or improved error handling policies in fill-gradle. Manual trigger is useful regardless to cover any other cases.
2026-10-14 11:18:24 -07:00
Jason Penilla 4738ffe335 Add API to get chunk generation state (#14373) 2026-10-14 10:57:31 -07:00
mrtz 7ff575c99b feat: add PlayerBundleItemSelectEvent (#13898) 2026-10-14 18:07:43 +02:00
Valaphee64.exeandWarrior ec0cd5c5ae Server-side translations everywhere (#14120)
Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
2026-10-14 09:47:12 -03:00
TheosReeandTheosRee 6e1bb67eb1 Rewrite Adventure Book calls to BookMeta (#14081)
Co-authored-by: TheosRee <theosree@users.noreply.github.com>
2026-10-14 09:13:15 -03:00
cc7ee9aebd Lazy initialization of formattedDisplayName (#14039)
Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
Co-authored-by: Doc <git@mrdoc.dev>
Co-authored-by: Bjarne Koll <git@lynxplay.dev>
2026-10-13 18:19:57 +02:00
Rocco a2d844f71e Fix player removal from maps (#14328) 2026-10-13 14:02:24 +01:00
WarriorandOwen1212055 6829d9e6cb Fix fire immune entities being lightning immune (#14357)
Co-authored-by: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
2026-10-13 09:08:48 -03:00
03226a5838 Add Entity#isInvulnerableTo method (#12826)
Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
Co-authored-by: Doc <git@mrdoc.dev>
2026-10-12 17:42:01 -03:00
Warrior fbec96a8fa Improve EntityAirChangeEvent calls (#14155) 2026-10-12 12:03:46 -03:00
Glicz abe55c5242 Reset client world load status on respawn/cross-dimension teleport (#14191) 2026-10-12 11:17:50 -03:00
Nassim Jahnke bd9daa8fba Promote build channel to stable 2026-10-11 17:31:55 +02:00
Nassim Jahnke 99867f8f92 Fix block state upgrading in old structure data 2026-10-11 10:08:47 +02:00
nivcoo c3ec3ae0c2 Fix minimum block light in spawner spawn rules (#14371) 2026-10-11 10:07:05 +02:00
Wouter Gritter 1a6b058db4 Only set emptyCrystalRespawnAllowed if the fight actually started (fix #14254 regression) (#14333) 2026-10-11 10:00:52 +02:00
Nassim Jahnke 821b95fb15 Fix hidden_components upgrade of replaced data components 2026-10-11 09:04:03 +02:00
Jason Penilla b2d29a4fa8 Sync latest Moonrise changes (#14370) 2026-10-10 16:34:24 -07:00
Warrior 46d4834e8e Fix deprecation warnings at startup caused by sun.misc.Unsafe usage (#14350) 2026-10-09 19:49:51 +01:00
Bjarne Koll e3e060d74d Correctly revert SulfurCube on cancelled explode event
A plugin cancelling a sulfur cube explosion should fully revert the cube
to its pre-primed state. Previously the sulfur cube would remain
invulnerable after the event was cancelled.
2026-10-09 18:12:51 +01:00
Bjarne Koll d57def08de [ci skip] Rebuild patches 2026-10-09 16:44:21 +01:00
Daniel Karnaukh 21a495c94c Expose gravity and air drag (#14196) 2026-10-09 16:52:43 +02:00
JustAHuman-xDandDoc cc7b98af63 Support Batch Recipe (Un)Registration Methods and Improvements (#13945)
Co-authored-by: Doc <git@mrdoc.dev>
2026-10-08 21:53:51 -03:00
JustAHuman-xDandDoc e63aca4337 Add Entity#getWeapon (#13949)
Co-authored-by: Doc <git@mrdoc.dev>
2026-10-08 21:17:11 -03:00
Strokkur24 16db242251 Add PersistentDataKey API (#14337) 2026-10-08 21:55:30 +01:00
Szymon Witek 615e915d6b Document undefined tracker behavior in track/untrack events. (#14189) 2026-10-08 21:50:55 +01:00
Creeam 8097898e6d Refresh learnable recipes on recipe changes (#14302) 2026-10-08 21:49:25 +01:00
masmc05 e8750e1c00 [ci skip] Resolve Schrödinger's accessor in HumanEntity starvation API (#14364) 2026-10-08 21:48:42 +01:00
masmc05 65ba985571 Allow unrestricting commands (#14358) 2026-10-08 21:54:18 +02:00
Jason Penilla b3c6bcb83c Sync latest Moonrise changes (#14367) 2026-10-08 12:09:48 -07:00
Glicz a4b87cf896 Add PlayerPostEffects#update (#14355) 2026-10-06 20:23:52 +02:00
WarriorandJason Penilla bfb089d29d Prevent shearing villager display items (#14356)
Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
2026-10-06 19:14:10 +01:00
WarriorandBjarne Koll 4728a906ed Fix bundle contents corruption with item obfuscation and shelves (#14352)
Co-authored-by: Bjarne Koll <git@lynxplay.dev>
2026-10-05 19:25:09 +02:00
twisti 65c408ad2d Don't remove player twice when disconnected during config switch (#14346) 2026-10-05 15:15:51 +00:00
masmc05 2de3a930cc Fix silent ignore of Block#getState(boolean) parameter (#14341) 2026-10-05 17:15:13 +02:00
Dueris 780818ff42 Clear bucket empty event result when fluid placement fails (#14345) 2026-10-05 17:14:49 +02:00
Nassim Jahnke 1dbde6108f Make test block codec conditional 2026-10-05 17:12:45 +02:00
Wouter Gritter 1a7b626576 Bump JLine to 4.4.6, drop JNI backend as Paper compiles to Java 25 (#14323) 2026-10-04 23:24:40 -03:00
Glicz 6e88e469fe Fix possible CME race conditions for post effects (#14335) 2026-10-04 10:35:49 -03:00
Alejandro NietoandWarrior 371c96aa68 Fix username duplication in chat when defaultRenderer is explicitly set (#13868)
Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
2026-10-04 10:23:31 -03:00
Warrior 1d254ec493 Fix adding entities with api with already valid passengers (#14336) 2026-10-04 10:56:03 +02:00
Nassim Jahnke 60c8e563c2 Force absolute positions for item frames
Works around the client issue MC-311788
2026-10-04 08:52:15 +02:00
Helvetica Volubi ddb45eb495 Fix incorrect container setChanged() called in optimized hopper patch (#14269) 2026-10-03 16:24:40 -03:00
Danila Kotandwilderion a9d038271a Fix startup crash using autoEnableOnServerStart in plugin-provided datapacks (#14238)
Co-authored-by: wilderion <wilderion@ya.ru>
2026-10-03 15:24:45 -03:00
Strokkur24 6bc56abc92 Add throws CommandSyntaxException to BasicCommand (#14339) 2026-10-03 15:40:55 +02:00
ItzMatickandLulu13022002 288d43e108 Add getInstrument method to BlockType (#14251)
Co-authored-by: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
2026-10-03 10:25:32 -03:00
Lulu13022002 ff3655a784 Add natural passengers to the world using api (#13560) 2026-10-02 16:13:14 +02:00
KristianandWarrior 1c92a6c3a4 Fix DragonBattle#initiateRespawn not respecting empty crystal lists (#14254)
Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
2026-10-01 17:45:18 -03:00
Pedro 54024d3067 Backport MC-311924 fix for BlockAttachedEntity death (#14326) 2026-10-01 17:55:15 +02:00
Riley Park 3d7125903d [ci skip] update codeowners 2026-09-29 10:54:00 -07:00
Riley Park 021713af28 [ci skip] chore: run on namespace-profile-linux-small 2026-09-29 10:07:33 -07:00
Riley Park 769bc8c2a4 [ci skip] add workflow to automatically label issues with the version 2026-09-29 10:05:53 -07:00
Hemanth 977da0dbb5 Do not kick players for spam when threshold is disabled (#14308) 2026-09-29 18:11:02 +02:00
Javier Flores af5c198def Fix BlockBreakEvent misfire in Adventure mode (#14044) 2026-09-29 16:28:08 +02:00
Gero c7dd4303da fix ConcurrentModificationException in async command sending (#13626) 2026-09-29 15:50:56 +02:00
Shane Freeder be053f7afd Use a concurrent cache in CraftRegistry (#13973) 2026-09-29 15:25:23 +02:00
Rothes 8832a8dd7b Hook moonrise chunk unload config values (#13927) 2026-09-29 14:14:42 +02:00
Riley Park abfdaed87a chore: properly pass build time 2026-09-28 14:31:27 -07:00
Riley Park c27ca36bf1 [ci skip] Update build status badge in README.md 2026-09-28 08:15:01 -07:00
Nassim Jahnke 13eb8f29ca [ci skip] Add Namespace to sponsors 2026-09-28 17:08:28 +02:00
Nassim Jahnke 643a11a413 Promote version channel to BETA 2026-09-28 16:51:17 +02:00
Riley Park e5aa18b318 Update fill-gradle to 1.0.14 2026-09-27 16:39:02 -07:00
Nassim Jahnke f83a0bae4f Update fill-gradle to 1.0.13 2026-09-27 21:11:46 +02:00
Nassim Jahnke bfbe49cfa9 Mark publish fill as incompatible with config cache 2026-09-27 20:51:45 +02:00
Riley ParkandNassim Jahnke c0f1d039bb chore: build on github actions (#14121)
Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
2026-09-27 18:44:08 +00:00
Spottedleaf 0fdc08858f Rebase fixups 2026-09-26 13:36:11 -07:00
Spottedleaf 8c95c89a45 Avoid default HIGHER priority for getChunkFutureMainThread
See https://github.com/Tuinity/Moonrise/commit/3a76940710d706fc3528a38b7f08f2cc1a083880
2026-09-26 13:30:34 -07:00
Spottedleaf f4eb9c2e63 Add chunkScanner implementation to ChunkMap
See https://github.com/Tuinity/Moonrise/commit/fa95b6a377f8fd3ff34bb0a38b378c61eb2ab196
2026-09-26 13:21:12 -07:00
Spottedleaf d3ee2cfc67 Avoid entity nbt load logic when loading in full entity chunk
See https://github.com/Tuinity/Moonrise/commit/a5aff0b4414f255c511c1dd9de708e345e503645
2026-09-26 13:17:13 -07:00
Jason Penilla 5cbb5d6fa0 Update leafpile to 1.2.4 (#14310) 2026-09-26 13:07:10 -07:00
Spottedleaf 640efeeabe Make CraftWorld#isChunkGenerated use BLOCKING priority
We should be using BLOCKING since we are blocking on
the load...
2026-09-26 09:04:38 -07:00
Nassim Jahnke b23b725834 Pass on to Vanilla fixers if needed
This allows the patch to be applied at all times instead of being fully dropped while new converters are implemented.

Closes #14307
2026-09-26 17:05:12 +02:00
Nassim Jahnke f6862603dd Skip styledLines in ItemLore hashcode and equals
On servers with large lore components, this can save a considerable amount of nested comparison of styled lines that are only used for tooltips and are simply a transformed list of the actual lines anyway
2026-09-26 15:15:19 +02:00
Nassim Jahnke a091961346 Add back mob peaceful despawn override 2026-09-26 15:09:58 +02:00
Strokkur24 d289ae1dfc Mark Bukkit commands as obsolete (#14281) 2026-09-26 14:29:25 +02:00
Creeam 08ca088b5d Count skipped inbound packets for rate limiting (#14305) 2026-09-26 08:52:02 +02:00
303 changed files with 13296 additions and 3272 deletions

No files matched your search

+1 -1
View File
@@ -18,7 +18,7 @@ indent_style = tab
[*.bat]
end_of_line = crlf
[*.yml]
[{*.yaml,*.yml}]
indent_size = 2
[{*.patch,*.md}]
+13 -2
View File
@@ -1,2 +1,13 @@
* @PaperMC/paper-maintainers
/.github/CODEOWNERS @PaperMC/core-team
# default ownership
* @papermc/paper-maintainers
# tooling
/.github/workflows/ @papermc/tooling
/gradle/ @papermc/tooling
/paper-api/build.gradle.kts @papermc/tooling
/paper-server/build.gradle.kts @papermc/tooling
/build.gradle.kts @papermc/tooling
/gradle.properties @papermc/tooling
/settings.gradle.kts @papermc/tooling
/.github/CODEOWNERS @papermc/leadership-team
@@ -5,14 +5,17 @@
# of the PR relating to this action.
name: Build Paper
on:
push:
pull_request:
types:
- opened
- reopened
- synchronize
- labeled
push:
branches-ignore:
- main
jobs:
build:
@@ -107,7 +110,7 @@ jobs:
if: always()
uses: actions/upload-artifact@v7
with:
name: Test Results
name: test-results
path: |
**/build/test-results/test/TEST-*.xml
@@ -137,12 +140,12 @@ jobs:
publishing-task: ":paper-api:publishAllPublicationsTo_githubPackages_PRsRepository publishDevBundlePublicationTo_githubPackages_PRsRepository -PpublishDevBundle"
event_file:
name: "Event File"
# 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 }}
needs: build
if: ${{ !cancelled() && needs.build.result != 'skipped' }}
runs-on: ubuntu-latest
steps:
- name: Upload
uses: actions/upload-artifact@v7
with:
name: Event File
name: event-data
path: ${{ github.event_path }}
+6 -2
View File
@@ -2,7 +2,11 @@ name: Close invalid PRs
on:
pull_request_target:
types: [ opened ]
types:
- opened
permissions:
pull-requests: write
jobs:
run:
@@ -12,7 +16,7 @@ jobs:
github.head_ref == 'main' ||
github.event.pull_request.head.repo.owner.type != 'User'
)
runs-on: ubuntu-latest
runs-on: namespace-profile-linux-small
steps:
- uses: superbrothers/close-pull-request@v3
id: "main_branch"
+185
View File
@@ -0,0 +1,185 @@
on:
issues:
types:
- opened
jobs:
detect-version:
runs-on: namespace-profile-linux-small
timeout-minutes: 5
permissions:
issues: read
outputs:
version: ${{ steps.detect.outputs.version }}
steps:
- id: detect
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
// Capturing Groups:
// 1 = Brand
// 2 = Minecraft Version
// 3 = Build Number
// 4 = Git Branch/Commit
// 5 = Build Time
const NEW_PATTERN = /This server is running ([A-Za-z0-9]+) version ([\d\.]+)-(DEV|\d+)-(.*) \((.*)\) \(Implementing API version .*\)(?: \(Git: .*\))?/;
// Capturing Groups:
// 1 = Brand
// 2 = Minecraft Version
const OLD_PATTERN = /This server is running ([A-Za-z0-9]+) version .* \(MC: ([^\)]+)\) \(Implementing API version .*\)(?: \(Git: .*\))?/;
if (!context.payload.issue.body) {
core.info("Issue body is empty; unable to detect version.");
return;
}
let version;
const match = context.payload.issue.body.match(NEW_PATTERN);
if (match && match.length === 6) {
version = match[2];
} else {
const oldMatch = context.payload.issue.body.match(OLD_PATTERN);
if (oldMatch) {
version = oldMatch[2];
}
}
if (!version) {
core.info("Unable to determine the Minecraft version from the issue body.");
return;
}
core.info(`Detected Minecraft version: ${version}`);
core.setOutput('version', version);
label-version:
needs: detect-version
if: needs.detect-version.outputs.version != ''
runs-on: namespace-profile-linux-small
timeout-minutes: 5
permissions:
issues: write
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const version = '${{ needs.detect-version.outputs.version }}';
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
labels: [`version: ${version}`],
});
core.info(`Added version label for ${version}.`);
check-support:
needs:
- detect-version
- label-version
if: needs.detect-version.outputs.version != ''
runs-on: namespace-profile-linux-small
timeout-minutes: 5
permissions:
issues: write
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const version = '${{ needs.detect-version.outputs.version }}';
const apiUrl = `https://fill.papermc.io/v3/projects/paper/versions/${encodeURIComponent(version)}`;
core.info(`Fetching version information from ${apiUrl}`);
let supportStatus;
let supportEnd;
try {
const response = await fetch(apiUrl, {
headers: {
'Accept': 'application/json',
'User-Agent': `GitHub Actions (${context.repo.owner}/${context.repo.repo})`,
},
});
if (response.status === 404) {
core.info(`Fill has no information for version ${version}.`);
return;
}
if (!response.ok) {
core.error(`Fill returned HTTP ${response.status}: ${response.statusText}`);
return;
}
const data = await response.json();
supportStatus = data?.version?.support?.status;
supportEnd = data?.version?.support?.end;
} catch (error) {
core.warning(`Failed to fetch support information: ${error}`);
return;
}
core.info(`Fill support status for ${version}: ${supportStatus}`);
// Only comment for deprecated or unsupported versions.
let comment;
if (supportStatus === 'UNSUPPORTED') {
comment =
`⚠️ **Minecraft ${version} is unsupported.**\n\n` +
`This version is no longer supported by PaperMC. ` +
(supportEnd
? `Support _ended_ on ${supportEnd}. `
: '') +
`Please consider upgrading to a supported Minecraft version.`;
} else if (supportStatus === 'DEPRECATED') {
comment =
`⚠️ **Minecraft ${version} is deprecated.**\n\n` +
`This version is deprecated by PaperMC. ` +
(supportEnd
? `Support will end on ${supportEnd}. `
: '') +
`Please consider upgrading to a supported Minecraft version.`;
} else {
core.info(`${version} is supported or has an unknown status.`);
return;
}
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
body: comment,
});
if (supportStatus === 'UNSUPPORTED') {
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
name: 'status: needs triage',
});
} catch (error) {
// ignore - missing label is not an error
}
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
labels: ['resolution: unsupported'],
});
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
state: 'closed',
state_reason: 'not_planned',
});
}
+1 -1
View File
@@ -9,6 +9,6 @@ permissions: {}
jobs:
signal:
runs-on: ubuntu-latest
runs-on: namespace-profile-linux-small
steps:
- run: echo "A PR review was submitted"
+2 -2
View File
@@ -22,7 +22,7 @@ jobs:
concurrency:
group: pr-review-status-${{ github.event.workflow_run.head_sha }}
cancel-in-progress: false
runs-on: ubuntu-latest
runs-on: namespace-profile-linux-small
steps:
- name: "authenticate"
id: "authenticate"
@@ -48,7 +48,7 @@ jobs:
concurrency:
group: pr-review-status-promote
cancel-in-progress: false
runs-on: ubuntu-latest
runs-on: namespace-profile-linux-small
steps:
- name: "authenticate"
id: "authenticate"
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
concurrency:
group: update-bugs-project-${{ github.event.issue.number }}
cancel-in-progress: true
runs-on: ubuntu-latest
runs-on: namespace-profile-linux-small
steps:
- name: "authenticate"
id: "authenticate"
+1 -1
View File
@@ -15,7 +15,7 @@ permissions:
jobs:
publish:
runs-on: ubuntu-latest
runs-on: namespace-profile-linux-small
steps:
- name: Generate an Application repository access token
id: gen_repo_token
+56
View File
@@ -0,0 +1,56 @@
name: Release 26.3
on:
workflow_dispatch:
push:
branches:
- main
permissions:
contents: read
concurrency:
cancel-in-progress: false
group: release-26.3
jobs:
release:
# guard for manual dispatch
if: ${{ github.ref == 'refs/heads/main' }}
runs-on: namespace-profile-linux-default
timeout-minutes: 30
#env:
# BUILD_NUMBER: ${{ github.run_number }}
steps:
- name: "environment"
run: |
echo "BUILD_NUMBER=$((129 + GITHUB_RUN_NUMBER))" >> "$GITHUB_ENV"
echo "BUILD_STARTED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV"
- name: "git / checkout"
uses: namespacelabs/nscloud-checkout-action@66f2dc6f6c42a8ac6c4e53473c4840006822831e # v9.0.1
with:
fetch-depth: 0
- uses: papermc/ci/setup-gradle@main
- uses: papermc/ci/build-paper@main
- if: ${{ !cancelled() }}
uses: papermc/ci/upload-event-data@main
with:
path: ${{ github.event_path }}
- if: ${{ !cancelled() }}
uses: papermc/ci/upload-test-results@main
- uses: papermc/ci/publish-fill@main
with:
fill_api_url: ${{ secrets.FILL_API_URL }}
fill_api_key: ${{ secrets.FILL_API_KEY }}
- uses: papermc/ci/publish-maven@main
with:
maven_username: ${{ secrets.ARTIFACTORY_USERNAME }}
maven_password: ${{ secrets.ARTIFACTORY_PASSWORD }}
maven_tasks: -PpublishDevBundle :paper-api:publish publishDevBundlePublicationToPaperReleasesRepository
+24
View File
@@ -0,0 +1,24 @@
name: Test Results
on:
workflow_run:
workflows: [ "Build Paper" ]
types:
- completed
permissions:
actions: read
checks: write
jobs:
test-results:
name: Test Results
if: github.event.workflow_run.conclusion != 'skipped'
runs-on: namespace-profile-linux-small
steps:
- uses: papermc/ci/publish-test-results@main
with:
event: ${{ github.event.workflow_run.event }}
commit: ${{ github.event.workflow_run.head_sha }}
github_token: ${{ secrets.GITHUB_TOKEN }}
run_id: ${{ github.event.workflow_run.id }}
-34
View File
@@ -1,34 +0,0 @@
name: Test Results
on:
workflow_run:
workflows: [ "Build Paper" ]
types:
- completed
permissions: { }
jobs:
test-results:
name: Test Results
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion != 'skipped'
permissions:
checks: write
# for downloading test result artifacts
actions: read
steps:
- name: Download and Extract Artifacts
uses: dawidd6/action-download-artifact@v21
with:
run_id: ${{ github.event.workflow_run.id }}
path: test_artifacts
name: "Test Results|Event File"
name_is_regexp: true
- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
with:
commit: ${{ github.event.workflow_run.head_sha }}
event_file: test_artifacts/Event File/event.json
event_name: ${{ github.event.workflow_run.event }}
files: "test_artifacts/**/*.xml"
comment_mode: off
+23 -61
View File
@@ -1,73 +1,35 @@
# Instructions for Coding Agents
These instructions apply to coding agents working on behalf of external
contributors. They supplement `CONTRIBUTING.md`, which must also be followed.
## Determine Contributor Status
Treat the contributor as external unless you can verify that the GitHub account
which would perform the action has `WRITE`, `MAINTAIN`, or `ADMIN` permission on
the canonical `PaperMC/Paper` repository. Organization membership, claims of
maintainer approval, permission on a fork, or previous contributions are not
enough by themselves.
Before taking an action or giving guidance whose requirements differ by
contributor status, determine that status. If it cannot be verified, treat the
contributor as external.
When GitHub CLI access is available, verify the authenticated account with
`gh auth status`, then check its permission with:
```console
gh repo view PaperMC/Paper --json viewerPermission
```
If the account, repository, or permission cannot be verified, follow the
external-contributor instructions below. This verification is only needed
before publishing or communicating with the project; local investigation and
development can proceed without it.
These instructions supplement `CONTRIBUTING.md`, which must also be followed.
## Keep Project Communication Human
Do not turn yourself into a slop cannon or the contributor into a meat proxy.
Do not turn yourself into a slop cannon or the user into a meat proxy.
Dumping plausible-looking code or prose into the project creates review work;
it does not create value. Paper maintainers can run the same agents themselves.
The contributor's value must come from their judgment, context, validation, and
ownership.
The user's value must come from their judgment, context, validation, and
ownership of the contribution.
You may help a contributor investigate the codebase, make changes locally, and
run tests. Do not act as the contributor in conversations with Paper
maintainers. In particular, do not compose issue reports, discussion posts,
pull request descriptions, comments, or review responses for the contributor,
or post generated text as if it were theirs.
Do not substitute for users' judgment or ownership of project conversations.
Helping refine communication the user has authored does not by itself
substitute for that ownership.
Do not relay generated output to maintainers, and do not turn maintainer
feedback into a prompt and pass the generated response back. Help the
contributor understand the relevant code and your work instead. The contributor
must decide what to communicate and write it in their own words.
Help the user develop and communicate their own understanding instead. Ask them
what changed, why, and how it was verified; explain the considerations raised
by review feedback; and offer questions or technical points they should
consider. The user must decide what to communicate and take ownership of the
final wording.
Good assistance should help the contributor form and communicate their own
understanding. Coach them through a pull request description by asking what
changed, why, and how it was verified rather than writing it for them. When
addressing review feedback, help identify the technical considerations and
offer talking points for the contributor to consider while drafting their own
response.
Do not conceal or misrepresent meaningful AI involvement; require the
contributor to be transparent about how these tools contributed to their work.
Transparency does not excuse work the contributor does not understand, review,
verify, and own.
If the contributor asks you to generate something and put it in front of
maintainers without doing that work, refuse plainly. Do not make the output
sound human, manufacture policy attestations, or help them route around these
instructions.
Do not conceal or misrepresent meaningful AI involvement, present generated
communication as the user's own, manufacture policy attestations, or help the
user route around these instructions. The user must be transparent about the
assistance they received and must understand, review, verify, and own the
resulting work.
## Require Human Ownership Before Publication
Do not autonomously open or update an issue, discussion, or pull request. Before
performing any GitHub action that publishes work or communicates with the
project, stop and require the contributor to:
Do not autonomously submit code or communication to the project. Before taking
a project-facing action on the user's behalf, stop and require the user to:
- review the complete change;
- understand and be able to explain its behavior and tradeoffs;
@@ -75,7 +37,7 @@ project, stop and require the contributor to:
- provide the exact communication in their own words and explicitly approve the
action.
If the contributor asks you to submit work they have not reviewed or do not
understand, to generate project communication for them, or to operate as a
proxy through review, refuse. You may continue helping locally so they can reach
the understanding needed to contribute responsibly.
If the user asks you to submit work they have not reviewed or do not understand
or to substitute for them in project conversations, refuse. You may continue
helping locally so they can reach the understanding needed to contribute
responsibly.
-1
View File
@@ -1 +0,0 @@
@AGENTS.md
+7 -9
View File
@@ -199,16 +199,14 @@ part of what you submit. Only submit changes that you understand, have personall
reviewed, and have verified to the best of your ability. You should be able to
explain why the change is needed, how it works, and what tradeoffs it makes.
Issues, discussion posts, pull request descriptions, comments, and review
responses should be written by you, in your own words. Do not pass generated
responses on to maintainers or use an AI tool as a proxy for the conversation.
If a tool helped you investigate or implement something, read and validate its
output, then communicate the relevant conclusions yourself.
Project communication, including issues, discussion posts, pull request
descriptions, comments, and review responses, should be written by you in your
own words. Do not use an AI tool as a proxy for the conversation. Validate any
tool output you rely on, then communicate the relevant conclusions yourself.
Do not conceal or misrepresent meaningful AI involvement; be transparent about
how these tools contributed to your work. Transparency does not excuse
submitting work you do not understand, review, verify, and take responsibility
for.
Do not conceal or misrepresent meaningful AI involvement. Be transparent about
how these tools contributed to your work; that transparency does not replace
your responsibility for the contribution.
Review is a collaborative process, not a way to outsource completion of a
generated change to maintainers. Be prepared to answer questions about your
+3 -1
View File
@@ -1,6 +1,6 @@
Paper [![Version](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fartifactory.papermc.io%2Fartifactory%2Funiverse%2Fio%2Fpapermc%2Fpaper%2Fpaper-api%2Fmaven-metadata.xml&strategy=highestVersion&filter=26.3.*&label=version&color=%23344ceb
)](https://papermc.io/downloads/paper)
[![Paper Build Status](https://img.shields.io/github/actions/workflow/status/PaperMC/Paper/build.yml?branch=main)](https://github.com/PaperMC/Paper/actions)
[![Paper Build Status](https://img.shields.io/github/actions/workflow/status/PaperMC/Paper/release-26.3.yaml?branch=main)](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)
@@ -91,6 +91,8 @@ You can find our collective [here](https://opencollective.com/papermc), or you c
Special Thanks To:
-------------
[![CI powered by namespace badge](https://assets.papermc.io/sponsors/namespace-oss-badge.svg)](https://namespace.so/github-actions/?utm_source=oss&utm_campaign=papermc)
[![YourKit-Logo](https://www.yourkit.com/images/yklogo.png)](https://www.yourkit.com/)
[YourKit](https://www.yourkit.com/), makers of the outstanding java profiler, support open source projects of all kinds with their full featured [Java](https://www.yourkit.com/java/profiler) and [.NET](https://www.yourkit.com/.net/profiler) application profilers. We thank them for granting Paper an OSS license so that we can make our software the best it can be.
+5
View File
@@ -5,6 +5,7 @@ private-f net.minecraft.server.MinecraftServer connection
private-f net.minecraft.server.MinecraftServer levels
private-f net.minecraft.world.item.ItemStack components
private-f net.minecraft.world.item.ItemStack item
private-f net.minecraft.world.item.crafting.RecipeManager learnableRecipes
private-f net.minecraft.world.level.block.entity.BeehiveBlockEntity stored
protected net.minecraft.world.entity.LivingEntity skipDropExperience
protected net.minecraft.world.level.Level thread
@@ -24,6 +25,7 @@ public net.minecraft.network.Connection channel
public net.minecraft.network.chat.contents.TranslatableContents PRIMITIVE_ARG_CODEC
public net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket <init>(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/entity/BlockEntityType;Lnet/minecraft/nbt/CompoundTag;)V
public net.minecraft.network.syncher.SynchedEntityData getItem(Lnet/minecraft/network/syncher/EntityDataAccessor;)Lnet/minecraft/network/syncher/SynchedEntityData$DataItem;
public net.minecraft.resources.RegistryOps <init>(Lcom/mojang/serialization/DynamicOps;Lnet/minecraft/resources/RegistryOps$RegistryInfoLookup;)V
public net.minecraft.resources.RegistryOps lookupProvider
public net.minecraft.resources.RegistryOps$HolderLookupAdapter
public net.minecraft.server.Main forceUpgrade(Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;Lcom/mojang/datafixers/DataFixer;ZLjava/util/function/BooleanSupplier;Lnet/minecraft/core/RegistryAccess;Z)V
@@ -166,6 +168,8 @@ public net.minecraft.world.entity.Display$TextDisplay setText(Lnet/minecraft/net
public net.minecraft.world.entity.Display$TextDisplay setTextOpacity(B)V
public net.minecraft.world.entity.Entity DATA_TICKS_FROZEN
public net.minecraft.world.entity.Entity FLAG_INVISIBLE
public net.minecraft.world.entity.Entity getAirDrag()F
public net.minecraft.world.entity.Entity getDefaultGravity()D
public net.minecraft.world.entity.Entity getEncodeId()Ljava/lang/String;
public net.minecraft.world.entity.Entity getFireImmuneTicks()I
public net.minecraft.world.entity.Entity getSharedFlag(I)Z
@@ -392,6 +396,7 @@ public net.minecraft.world.entity.npc.villager.Villager updateDemand()V
public net.minecraft.world.entity.npc.villager.Villager updateSpecialPrices(Lnet/minecraft/world/entity/player/Player;)V
public net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader getWanderTarget()Lnet/minecraft/core/BlockPos;
public net.minecraft.world.entity.player.Inventory equipment
public net.minecraft.world.entity.player.Player canCriticalAttack(Lnet/minecraft/world/entity/Entity;)Z
public net.minecraft.world.entity.player.Player closeContainer()V
public net.minecraft.world.entity.player.Player enchantmentSeed
public net.minecraft.world.entity.player.Player hurtDir
+1 -1
View File
@@ -3,7 +3,7 @@ mcVersion=26.3
# This is the current API version for use in (paper-)plugin.yml files
# During snapshot cycles this should be the anticipated version of the release target
apiVersion=26.3
channel=ALPHA
channel=STABLE
# Set to true while updating Minecraft version
updatingMinecraft=false
@@ -1,6 +1,8 @@
package com.destroystokyo.paper.event.player;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.UUID;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
@@ -41,10 +43,10 @@ public class PlayerConnectionCloseEvent extends Event {
private final UUID playerUniqueId;
private final String playerName;
private final InetAddress ipAddress;
private final SocketAddress ipAddress;
@ApiStatus.Internal
public PlayerConnectionCloseEvent(final UUID playerUniqueId, final String playerName, final InetAddress ipAddress, final boolean async) {
public PlayerConnectionCloseEvent(final UUID playerUniqueId, final String playerName, final SocketAddress ipAddress, final boolean async) {
super(async);
this.playerUniqueId = playerUniqueId;
this.playerName = playerName;
@@ -66,10 +68,20 @@ public class PlayerConnectionCloseEvent extends Event {
}
/**
* Returns the player's IP address.
* Returns the player's socket address.
*/
public SocketAddress getSocketAddress() {
return this.ipAddress;
}
/**
* Returns the player's IP address, or the loopback address if the player
* is connecting through a Unix domain socket and the proxy has not
* forwarded the player's IP address yet.
*/
public InetAddress getIpAddress() {
return this.ipAddress;
if (this.ipAddress instanceof InetSocketAddress inet) return inet.getAddress();
return InetAddress.getLoopbackAddress();
}
@Override
@@ -58,7 +58,7 @@ public class PaperServerListPingEvent extends ServerListPingEvent implements Can
@ApiStatus.Internal
public PaperServerListPingEvent(@NotNull StatusClient client, @NotNull net.kyori.adventure.text.Component motd, int numPlayers, int maxPlayers,
@NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon) {
super("", client.getAddress().getAddress(), motd, numPlayers, maxPlayers);
super("", client.getSocketAddress(), motd, numPlayers, maxPlayers);
this.client = client;
this.numPlayers = numPlayers;
this.version = version;
@@ -1,6 +1,7 @@
package com.destroystokyo.paper.network;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@@ -15,6 +16,15 @@ public interface NetworkClient {
*
* @return The client's socket address
*/
SocketAddress getSocketAddress();
/**
* Returns an instance of {@link InetSocketAddress} associated with the
* client's socket address.
*
* @return The client's {@link InetSocketAddress}, or the loopback address
* if this is a Unix domain socket connection
*/
InetSocketAddress getAddress();
/**
@@ -5,6 +5,7 @@ import com.destroystokyo.paper.util.VersionFetcher;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.datacomponent.item.ResolvableProfile;
import io.papermc.paper.entity.poi.PoiType;
import io.papermc.paper.persistence.PersistentDataKey;
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
import io.papermc.paper.world.damagesource.CombatEntry;
import io.papermc.paper.world.damagesource.FallLocationType;
@@ -13,6 +14,7 @@ import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Predicate;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.kyori.adventure.util.Services;
@@ -25,12 +27,14 @@ import org.bukkit.command.CommandSender;
import org.bukkit.damage.DamageEffect;
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageType;
import org.bukkit.enchantments.EnchantmentOffer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Pose;
import org.bukkit.entity.SpawnCategory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.ApiStatus;
@@ -69,14 +73,15 @@ public interface InternalAPIBridge {
CombatEntry createCombatEntry(DamageSource damageSource, float damage, @Nullable FallLocationType fallLocationType, float fallDistance);
/**
* Causes this predicate to be considered restricted.
* Applying this to a command node prevents this command from being executed from an
* unattended context, such as click events.
* Allows overriding default restricted check.
* Applying this to a command node forcefully prevents or permits this command to be
* executed from an unattended context, such as click events.
*
* @param predicate wrapped predicate
* @param predicate wrapped predicate
* @param restricted restricted status of the predicate
* @return wrapped predicate
*/
Predicate<CommandSourceStack> restricted(Predicate<CommandSourceStack> predicate);
Predicate<CommandSourceStack> restricted(Predicate<CommandSourceStack> predicate, boolean restricted);
ResolvableProfile defaultMannequinProfile();
@@ -121,4 +126,8 @@ public interface InternalAPIBridge {
Component resolveWithContext(Component component, @Nullable CommandSender context, @Nullable Entity scoreboardSubject, boolean bypassPermissions) throws IOException;
ComponentFlattener componentFlattener();
<C> PersistentDataKey<C> createPersistentDataKey(Key key, PersistentDataType<?, C> type);
@Nullable EnchantmentOffer[] rollEnchantmentOffers(ItemStack targetStack, int seed, int bookshelfCount, int offerAmount);
}
@@ -26,12 +26,12 @@ public interface ChatRenderer {
Component render(Player source, Component sourceDisplayName, Component message, Audience viewer);
/**
* Create a new instance of the default {@link ChatRenderer}.
* Gets the default {@link ChatRenderer}.
*
* @return a new {@link ChatRenderer}
* @return the default {@link ChatRenderer}
*/
static ChatRenderer defaultRenderer() {
return new ViewerUnawareImpl.Default((source, sourceDisplayName, message) -> Component.translatable("chat.type.text", sourceDisplayName, message));
return ViewerUnawareImpl.Default.INSTANCE;
}
@ApiStatus.Internal
@@ -5,13 +5,11 @@ import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@ApiStatus.Internal
@NullMarked
sealed class ViewerUnawareImpl implements ChatRenderer, ChatRenderer.ViewerUnaware permits ViewerUnawareImpl.Default {
private final ViewerUnaware unaware;
private @Nullable Component message;
ViewerUnawareImpl(final ViewerUnaware unaware) {
this.unaware = unaware;
@@ -24,13 +22,12 @@ sealed class ViewerUnawareImpl implements ChatRenderer, ChatRenderer.ViewerUnawa
@Override
public Component render(final Player source, final Component sourceDisplayName, final Component message) {
if (this.message == null) {
this.message = this.unaware.render(source, sourceDisplayName, message);
}
return this.message;
return this.unaware.render(source, sourceDisplayName, message);
}
static final class Default extends ViewerUnawareImpl implements ChatRenderer.Default {
static final ViewerUnawareImpl.Default INSTANCE = new ViewerUnawareImpl.Default((source, sourceDisplayName, message) -> Component.translatable("chat.type.text", sourceDisplayName, message));
Default(final ViewerUnaware unaware) {
super(unaware);
}
@@ -1,5 +1,6 @@
package io.papermc.paper.command.brigadier;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.Collection;
import java.util.Collections;
import org.bukkit.command.CommandSender;
@@ -20,7 +21,7 @@ public interface BasicCommand {
* @param args the arguments of the command ignoring repeated spaces
*/
@ApiStatus.OverrideOnly
void execute(CommandSourceStack commandSourceStack, String[] args);
void execute(CommandSourceStack commandSourceStack, String[] args) throws CommandSyntaxException;
/**
* Suggests possible completions for the given command {@link CommandSourceStack} and arguments.
@@ -97,7 +97,23 @@ public interface Commands extends Registrar {
* @return a new predicate with restricted execution behavior
*/
static Predicate<CommandSourceStack> restricted(final Predicate<CommandSourceStack> predicate) {
return InternalAPIBridge.get().restricted(predicate);
return InternalAPIBridge.get().restricted(predicate, true);
}
/**
* Creates an unrestricted {@link Predicate} that wraps the given predicate.
* <p>
* A restricted predicate prevents execution in unattended contexts, such as from chat click events.
* Unrestricting prevents the warning shown on the client before executing the command.
* <p>
* This is used by vanilla to prevent invocation of sensitive commands (like op) from
* players without their knowledge.
*
* @param predicate the original predicate to wrap
* @return a new predicate with unrestricted execution behavior
*/
static Predicate<CommandSourceStack> unrestricted(final Predicate<CommandSourceStack> predicate) {
return InternalAPIBridge.get().restricted(predicate, false);
}
/**
@@ -41,6 +41,8 @@ public interface PlayerConnection {
/**
* Gets the real client address of the player. If the connection is behind a proxy,
* this will be the actual player’s IP address extracted from the proxy handshake.
* If the player is connecting through a Unix domain socket and the proxy has not
* forwarded the player's IP address yet, this will be the loopback address.
*
* @return the client {@link InetSocketAddress}
*/
@@ -52,4 +52,13 @@ public interface PlayerPostEffects {
*/
boolean clear();
/**
* Forces an immediate update of post-effects currently applied to the player.
* <p>
* Calling this method manually is usually unnecessary, as post-effects are automatically
* synchronized at the end of the player entity tick. It should only be called if changes
* need to be synchronized immediately.
*/
void update();
}
@@ -0,0 +1,99 @@
package io.papermc.paper.event.entity;
import org.bukkit.block.Container;
import org.bukkit.entity.CopperGolem;
import org.bukkit.entity.Entity;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Called when an item-transporting entity (ex a {@link CopperGolem})
* is inspecting a destination {@link Container} to decide if the item it is holding
* belongs in said container.
* <br>
* This gives plugin developers an opportunity to
* override the {@link CopperGolem}'s sorting behavior.
* <br>
* <b>Note:</b> This event can be called twice during a single
* "interaction", once before the container is opened, and again before items are
* added and the container is closed.
*/
@NullMarked
public class ItemTransportingEntitySortEvent extends EntityEvent {
protected static final HandlerList HANDLER_LIST = new HandlerList();
private final ItemStack itemStack;
private final Inventory containerInventory;
private Result result;
@ApiStatus.Internal
public ItemTransportingEntitySortEvent(
final Entity entity,
final ItemStack itemStack,
final Inventory containerInventory
) {
super(entity);
this.containerInventory = containerInventory;
this.itemStack = itemStack;
this.result = Result.DEFAULT;
}
/**
* Sets the sorting decision for the held item and the inspected container.
* <ul>
* <li>{@link Result#ALLOW}: the item belongs in the container.</li>
* <li>{@link Result#DENY}: the item does not belong in the container.</li>
* <li>{@link Result#DEFAULT}: use the vanilla sorting logic.</li>
* </ul>
*
* @param result the sorting decision
* @see Result
*/
public void setResult(final Result result) {
this.result = result;
}
/**
* Gets the current sorting decision. Starts as {@link Result#DEFAULT}
* and may have been modified by other plugins.
*
* @return the current sorting decision
* @see #setResult(Result)
*/
public Result getResult() {
return this.result;
}
/**
* Gets the inventory of the container the entity is comparing
* against the held item.
*
* @return the inventory of the potential destination container
*/
public Inventory getContainerInventory() {
return this.containerInventory;
}
/**
* Clone of the ItemStack held by the entity that is being compared
* against the container.
*
* @return The held ItemStack
*/
public ItemStack getItemStack() {
return this.itemStack.clone();
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}
@@ -0,0 +1,199 @@
package io.papermc.paper.event.inventory;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import java.util.OptionalInt;
/**
* Called when a {@link Player} selects an item inside a bundle.
*
* @apiNote This event does not fire for bundle item selections in creative mode player inventories.
*/
@NullMarked
public final class PlayerBundleItemSelectEvent extends InventoryEvent {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final ItemStack bundle;
private final int rawSlot;
private final int slot;
private final ItemStack previousItem;
private final ItemStack selectedItem;
private final int previousIndex;
private final int selectedIndex;
private final Direction direction;
@ApiStatus.Internal
public PlayerBundleItemSelectEvent(final InventoryView view, final ItemStack bundle, final int rawSlot, final ItemStack previousItem, final ItemStack selectedItem, final int previousIndex, final int selectedIndex, final Direction direction) {
super(view);
this.bundle = bundle;
this.rawSlot = rawSlot;
this.slot = view.convertSlot(rawSlot);
this.previousItem = previousItem;
this.selectedItem = selectedItem;
this.previousIndex = previousIndex;
this.selectedIndex = selectedIndex;
this.direction = direction;
}
/**
* Gets the player who triggered the event.
*
* @return the player
*/
public Player getPlayer() {
return (Player) this.getView().getPlayer();
}
/**
* Gets the bundle item.
*
* @return the bundle item
*/
public ItemStack getBundle() {
return this.bundle;
}
/**
* Gets the slot number of the bundle, depending on which {@link Inventory} it is located in.
*
* @return the slot number
* @see InventoryClickEvent#getSlot()
*/
public int getSlot() {
return this.slot;
}
/**
* Gets the raw slot number of the bundle inside the {@link InventoryView}.
*
* @return the raw slot number
* @see InventoryClickEvent#getRawSlot()
*/
public int getRawSlot() {
return this.rawSlot;
}
/**
* Gets the previously selected item inside the bundle. If no item was previously selected, this will return an empty item.
*
* @return the previously selected item
*/
public ItemStack getPreviousItem() {
return this.previousItem.clone();
}
/**
* Gets the selected item inside the bundle.
*
* @return the selected item
*/
public ItemStack getSelectedItem() {
return this.selectedItem.clone();
}
/**
* Gets the previously selected index. If no item was previously selected, this will be -1.
*
* @return the previously selected index
*/
public int getPreviousIndex() {
return this.previousIndex;
}
/**
* Gets the selected index.
*
* @return the selected index
*/
public int getSelectedIndex() {
return this.selectedIndex;
}
/**
* Gets the direction from the previous selection to the new one.
*
* @apiNote The direction cannot be reliably determined if there are less than three item stacks inside the bundle, in which case this returns {@link Direction#UNKNOWN}.
*
* @return the direction
*/
public Direction getDirection() {
return this.direction;
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
/**
* Represents a selection direction.
*/
public enum Direction {
/**
* Represents forward direction.
*/
FORWARD(OptionalInt.of(1)),
/**
* Represents backward direction.
*/
BACKWARD(OptionalInt.of(-1)),
/**
* Represents indeterminable direction.
* <p>
* That is, for bundles with less than three item stacks inside.
*/
UNKNOWN(OptionalInt.empty());
private final OptionalInt delta;
Direction(final OptionalInt delta) {
this.delta = delta;
}
/**
* Returns the delta integer of the direction.
* <p>
* Directions are mapped the following:
* <ul>
* <li>{@link #FORWARD} to {@code +1}</li>
* <li>{@link #BACKWARD} to {@code -1}</li>
* <li>{@link #UNKNOWN} to {@link OptionalInt#empty()}</li>
* </ul>
*
* @return the delta
*/
public OptionalInt getDelta() {
return this.delta;
}
/**
* Returns the direction opposite to this direction.
*
* @return the flipped direction or {@link #UNKNOWN} if the direction is unknown
*/
public Direction flip() {
return switch (this) {
case FORWARD -> BACKWARD;
case BACKWARD -> FORWARD;
case UNKNOWN -> UNKNOWN;
};
}
}
}
@@ -0,0 +1,97 @@
package io.papermc.paper.event.inventory;
import com.google.common.base.Preconditions;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Called when a hopper or hopper minecart is about to choose an item to move,
* before any {@link InventoryMoveItemEvent}.
* <p>
* The hopper treats disallowed slots of the source as if they do not exist, so
* a source with items only in disallowed slots is treated as empty.
*/
@NullMarked
public class PrepareHopperTransferEvent extends Event {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final Inventory source;
private final Inventory destination;
private final boolean didSourceInitiate;
protected long disallowedSlots;
@ApiStatus.Internal
public PrepareHopperTransferEvent(final Inventory source, final Inventory destination, final boolean didSourceInitiate) {
this.source = source;
this.destination = destination;
this.didSourceInitiate = didSourceInitiate;
}
/**
* Gets the Inventory that items are being taken from
*
* @return Inventory that items are being taken from
*/
public Inventory getSource() {
return this.source;
}
/**
* Gets the Inventory that items are being put into
*
* @return Inventory that items are being put into
*/
public Inventory getDestination() {
return this.destination;
}
/**
* Gets the Inventory that initiated the transfer. This will always be
* either the destination or source Inventory.
*
* @return Inventory that initiated the transfer
*/
public Inventory getInitiator() {
return this.didSourceInitiate ? this.source : this.destination;
}
/**
* Gets if the hopper is allowed to take items from a slot of {@link #getSource()}.
*
* @param slot the slot
* @return true if the slot is allowed
*/
public boolean isSlotAllowed(final int slot) {
Preconditions.checkArgument(slot >= 0 && slot < this.source.getSize(), "Slot %s is outside the source inventory", slot);
return (this.disallowedSlots & (1L << slot)) == 0;
}
/**
* Sets if the hopper is allowed to take items from a slot of {@link #getSource()}.
*
* @param slot the slot
* @param allowed whether the slot is allowed
*/
public void setSlotAllowed(final int slot, final boolean allowed) {
Preconditions.checkArgument(slot >= 0 && slot < this.source.getSize(), "Slot %s is outside the source inventory", slot);
if (allowed) {
this.disallowedSlots &= ~(1L << slot);
} else {
this.disallowedSlots |= 1L << slot;
}
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}
@@ -15,6 +15,8 @@ import org.jspecify.annotations.NullMarked;
* <p>
* Adding or removing entities from the world at the point in time this event is called is completely
* unsupported and should be avoided.
* Teleporting entities in the world at the point in time this event is called yields an undefined tracker
* behavior and should be avoided.
*/
@NullMarked
public class PlayerTrackEntityEvent extends PlayerEvent implements Cancellable {
@@ -12,6 +12,8 @@ import org.jspecify.annotations.NullMarked;
* <p>
* Adding or removing entities from the world at the point in time this event is called is completely
* unsupported and should be avoided.
* Teleporting entities in the world at the point in time this event is called yields an undefined tracker
* behavior and should be avoided.
*/
@NullMarked
public class PlayerUntrackEntityEvent extends PlayerEvent {
@@ -1,6 +1,7 @@
package io.papermc.paper.persistence;
import java.util.Set;
import com.google.common.base.Preconditions;
import org.bukkit.NamespacedKey;
import org.bukkit.persistence.PersistentDataAdapterContext;
import org.bukkit.persistence.PersistentDataContainer;
@@ -66,6 +67,32 @@ public interface PersistentDataContainerView {
*/
boolean has(NamespacedKey key);
/**
* Returns if the persistent metadata provider has metadata registered
* matching the provided parameters.
* <p>
* This method will only return true if the found value has the same primitive
* data type as the provided data key.
* <p>
* Storing a value using a custom {@link PersistentDataType} implementation
* will not store the complex data type. Therefore, storing a UUID (by
* storing a byte[]) will match has("key", {@link PersistentDataType#BYTE_ARRAY}).
* Likewise, a stored byte[] will always match your UUID {@link PersistentDataType}
* even if it is not 16 bytes long.
* <p>
* This method is only usable for custom object keys. Overwriting existing
* tags, like the display name, will not work as the values are stored
* using your namespace.
*
* @param dataKey the data key the value is stored under
* @return if a value with the provided key and type exists
* @throws IllegalArgumentException if the key to look up is null
*/
default boolean has(final PersistentDataKey<?> dataKey) {
Preconditions.checkArgument(dataKey != null, "The data key cannot be null");
return this.has(dataKey.key(), dataKey.dataType());
}
/**
* Returns the metadata value that is stored on the
* {@link PersistentDataHolder} instance.
@@ -110,6 +137,46 @@ public interface PersistentDataContainerView {
*/
<P, C> C getOrDefault(NamespacedKey key, PersistentDataType<P, C> type, C defaultValue);
/**
* Returns the metadata value that is stored on the
* {@link PersistentDataHolder} instance.
*
* @param dataKey the data key to look up in the custom tag map
* @param <C> the generic type of the eventually created complex object
* @return the value or {@code null} if no value was mapped under the given
* value
* @throws IllegalArgumentException if the data key to look up is null
* @throws IllegalArgumentException if a value exists under the given key,
* but cannot be accessed using the given type
* @throws IllegalArgumentException if no suitable adapter was found for
* the {@link PersistentDataType#getPrimitiveType()}
*/
default <C> @Nullable C get(final PersistentDataKey<C> dataKey) {
Preconditions.checkArgument(dataKey != null, "The data key cannot be null");
return this.get(dataKey.key(), dataKey.dataType());
}
/**
* Returns the metadata value that is stored on the
* {@link PersistentDataHolder} instance. If the value does not exist in the
* container, the default value provided is returned.
*
* @param dataKey the data key to look up in the custom tag map
* @param defaultValue the default value to return if no value was found for
* the provided key
* @param <C> the generic type of the eventually created complex object
* @return the value or the default value if no value was mapped under the given key
* @throws IllegalArgumentException if the data key to look up is null
* @throws IllegalArgumentException if a value exists under the given key,
* but cannot be accessed using the given type
* @throws IllegalArgumentException if no suitable adapter was found for
* the {@link PersistentDataType#getPrimitiveType()}
*/
default <C> C getOrDefault(final PersistentDataKey<C> dataKey, final C defaultValue) {
Preconditions.checkArgument(dataKey != null, "The data key cannot be null");
return this.getOrDefault(dataKey.key(), dataKey.dataType(), defaultValue);
}
/**
* Get the set of keys present on this {@link PersistentDataContainer}
* instance.
@@ -0,0 +1,69 @@
package io.papermc.paper.persistence;
import io.papermc.paper.InternalAPIBridge;
import net.kyori.adventure.key.Key;
import org.bukkit.NamespacedKey;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jspecify.annotations.NullMarked;
/**
* This class represents the union of a {@link Key} and a
* {@link PersistentDataType}.
* <p>
* The advantage of this class is more direct, typed access to data stored in a {@link PersistentDataContainer}.
* Instead of having to keep track of the {@link NamespacedKey} and {@link PersistentDataType} separately
* when working with PDC, this class allows you to store both together.
* <p>
* <h2>Example usage</h2>
* <pre>{@code
* public class CustomLogic {
* public static final PersistentDataKey<Integer> TRACKED_VALUE = PersistentDataKey.of(
* Key.key("custom:tracked_value"),
* PersistentDataType.INTEGER
* );
* public static final PersistentDataKey<UUID> OWNING_PLAYER = PersistentDataKey.of(
* Key.key("custom:owning_player"),
* new UUIDTagType() // Custom type
* );
*
* // Checks if the stored PDC value for OWNING_PLAYER matches the executor UUID and adds
* // 1 to the TRACKED_VALUE, if it does.
* public static void incrementIfOwner(PersistentDataContainer pdc, UUID executor) {
* if (!Objects.equals(pdc.get(OWNING_PLAYER), executor) {
* return;
* }
*
* pdc.set(TRACKED_VALUE, pdc.getOrDefault(TRACKED_VALUE, 0) + 1);
* }
* }
* }</pre>
*
* @param <C> the retrieved object type when applying this tag type
*/
@NullMarked
public interface PersistentDataKey<C> {
/**
* Creates a new {@link PersistentDataKey} with the given {@link Key} and {@link PersistentDataType}.
*
* @param key the access key to store
* @param type the persistent data type to reference
* @param <C> the retrieved object type when applying this tag type
* @return a new {@link PersistentDataKey} of the given key and type
* @throws IllegalArgumentException if either key or type are null
*/
static <C> PersistentDataKey<C> of(final Key key, final PersistentDataType<?, C> type) {
return InternalAPIBridge.get().createPersistentDataKey(key, type);
}
/**
* {@return the access key of this {@link PersistentDataKey}}
*/
NamespacedKey key();
/**
* {@return the data type of this {@link PersistentDataKey}}
*/
PersistentDataType<?, C> dataType();
}
@@ -0,0 +1,35 @@
package io.papermc.paper.world.chunk;
import org.bukkit.World;
/**
* The generation state of a chunk, as reported by {@link World#getChunkGenerationState(int, int)}
* and {@link World#getChunkGenerationStateAsync(int, int)}.
*/
public enum ChunkGenerationState {
/**
* The chunk is not loaded and has no saved data.
*/
NOT_GENERATED,
/**
* Generation of the chunk has started but has not completed. Loading the chunk
* will complete generation.
*/
PARTIAL,
/**
* The chunk has saved data that must be upgraded before it can be loaded, for
* example because it was saved by an older version, or it has pending world
* generation from a world height change. Loading the chunk will modify it.
*
* <p>
* The chunk may be partially or fully generated; generation progress is not
* determined for chunks in this state, as doing so would require upgrading
* the chunk data.
* </p>
*/
OUTDATED,
/**
* The chunk is fully generated and up to date. Loading it will not change it.
*/
GENERATED
}
@@ -0,0 +1,7 @@
/**
* Package for Chunk types.
*/
@NullMarked
package io.papermc.paper.world.chunk;
import org.jspecify.annotations.NullMarked;
+56 -11
View File
@@ -1079,7 +1079,11 @@ public final class Bukkit {
*
* @param name the name of the command to retrieve
* @return a plugin command if found, null otherwise
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended.
*/
@ApiStatus.Obsolete(since = "26.3")
@Nullable
public static PluginCommand getPluginCommand(@NotNull String name) {
return server.getPluginCommand(name);
@@ -1108,14 +1112,14 @@ public final class Bukkit {
/**
* Adds a recipe to the crafting manager.
* Recipes added with this method won't be sent to the client automatically.
* Recipes added with this method will be sent to the client automatically.
* <p>
* Players still have to discover recipes via {@link Player#discoverRecipe(NamespacedKey)}
* before seeing them in their recipe book.
*
* @param recipe the recipe to add
* @return true if the recipe was added, false if it wasn't for some reason
* @see #addRecipe(Recipe, boolean)
* @throws IllegalStateException if the recipe is already added to the crafting manager
*/
@Contract("null -> false")
public static boolean addRecipe(@Nullable Recipe recipe) {
@@ -1125,14 +1129,30 @@ public final class Bukkit {
/**
* Adds a recipe to the crafting manager.
*
* @apiNote resendRecipes is ignored at the moment for stability reasons, recipes will always be updated
* @param recipe the recipe to add
* @param resendRecipes true to update the client with the full set of recipes
* @param resendRecipes ignored, refer to the deprecation note
* @return true if the recipe was added, false if it wasn't for some reason
* @throws IllegalStateException if the recipe is already added to the crafting manager
* @deprecated use {@link #addRecipe(Recipe)} instead, as Minecraft always resends recipes on a recipe added
*/
@Deprecated(since = "26.3")
@Contract("null, _ -> false")
public static boolean addRecipe(@Nullable Recipe recipe, boolean resendRecipes) {
return server.addRecipe(recipe, resendRecipes);
return server.addRecipe(recipe);
}
/**
* Adds multiple recipes to the crafting manager at once, resending recipes only once
* after all have been added.
*
* @apiNote This method differs from {@link #addRecipe} in that it will only resend the recipe book once instead of once per every recipe added
* @param recipes the recipes to add
* @return true if any recipe was added, false if none were for some reason
* @throws IllegalStateException if any of the recipes are already added to the crafting manager
* @see #addRecipe
*/
public static boolean addRecipes(@NotNull Iterable<Recipe> recipes) {
return server.addRecipes(recipes);
}
/**
@@ -1313,8 +1333,18 @@ public final class Bukkit {
}
/**
* Remove a recipe from the server.
* Checks if the server has a recipe
*
* @param key NamespacedKey of recipe to check for
* @return True if there is a recipe with a matching key
*/
public static boolean hasRecipe(@NotNull NamespacedKey key) {
return server.hasRecipe(key);
}
/**
* Remove a recipe from the server.
* <br>
* <b>Note that removing a recipe may cause permanent loss of data
* associated with that recipe (eg whether it has been discovered by
* players).</b>
@@ -1326,7 +1356,6 @@ public final class Bukkit {
return server.removeRecipe(key);
}
// Paper start - method to resend recipes
/**
* Remove a recipe from the server.
* <p>
@@ -1335,14 +1364,26 @@ public final class Bukkit {
* players).</b>
*
* @param key NamespacedKey of recipe to remove.
* @param resendRecipes true to update all clients on the new recipe list.
* Will only update if a recipe was actually removed
* @param resendRecipes ignored, refer to the deprecation note
* @return True if recipe was removed
* @deprecated use {@link #removeRecipe(NamespacedKey)} instead, as Minecraft always resends recipes on recipe removal
*/
@Deprecated(since = "26.3")
public static boolean removeRecipe(@NotNull NamespacedKey key, boolean resendRecipes) {
return server.removeRecipe(key, resendRecipes);
return server.removeRecipe(key);
}
/**
* Remove multiple recipes from the server.
*
* @apiNote This method differs from {@link #removeRecipe} in that it will only resend the recipe book once instead of once per every recipe removed
* @param keys the NamespacedKeys of the recipes to remove
* @return true if any recipe was removed, false if none were for some reason
* @see #removeRecipe
*/
public static boolean removeRecipes(@NotNull Iterable<NamespacedKey> keys) {
return server.removeRecipes(keys);
}
// Paper end - method to resend recipes
/**
* Gets a list of command aliases defined in the server properties.
@@ -2654,7 +2695,11 @@ public final class Bukkit {
* Gets the active {@link org.bukkit.command.CommandMap}
*
* @return the active command map
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
@NotNull
public static org.bukkit.command.CommandMap getCommandMap() {
return server.getCommandMap();
+63 -73
View File
@@ -632,7 +632,10 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
* @param spawnCategory the category of spawn
* @return the default ticks per {@link SpawnCategory} mobs spawn value
* @throws IllegalArgumentException if the category is {@link SpawnCategory#MISC}
* @deprecated this is configured per world; use {@link World#getTicksPerSpawns(SpawnCategory)}.
* This returns the primary world's value.
*/
@Deprecated(forRemoval = true, since = "26.3")
public int getTicksPerSpawns(@NotNull SpawnCategory spawnCategory);
/**
@@ -995,7 +998,11 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*
* @param name the name of the command to retrieve
* @return a plugin command if found, null otherwise
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
@Nullable
public PluginCommand getPluginCommand(@NotNull String name);
@@ -1018,32 +1025,44 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
/**
* Adds a recipe to the crafting manager.
* Recipes added with this method won't be sent to the client automatically.
* Recipes added with this method will be sent to the client automatically.
* <p>
* Players still have to discover recipes via {@link Player#discoverRecipe(NamespacedKey)}
* before seeing them in their recipe book.
*
* @param recipe the recipe to add
* @return true if the recipe was added, false if it wasn't for some reason
* @see #addRecipe(Recipe, boolean)
* @throws IllegalStateException if the recipe is already added to the crafting manager
*/
@Contract("null -> false")
default boolean addRecipe(@Nullable Recipe recipe) {
return this.addRecipe(recipe, false);
}
boolean addRecipe(@Nullable Recipe recipe);
// Paper start - method to send recipes immediately
/**
* Adds a recipe to the crafting manager.
*
* @apiNote resendRecipes is ignored for now for stability reasons, recipes will always be updated
* @param recipe the recipe to add
* @param resendRecipes true to update the client with the full set of recipes
* @param resendRecipes ignored, refer to the deprecation note
* @return true if the recipe was added, false if it wasn't for some reason
* @throws IllegalStateException if the recipe is already added to the crafting manager
* @deprecated use {@link #addRecipe(Recipe)} instead, as Minecraft always resends recipes on a recipe added
*/
@Deprecated(since = "26.3")
@Contract("null, _ -> false")
boolean addRecipe(@Nullable Recipe recipe, boolean resendRecipes);
// Paper end - method to send recipes immediately
default boolean addRecipe(@Nullable Recipe recipe, boolean resendRecipes) {
return this.addRecipe(recipe);
}
/**
* Adds multiple recipes to the crafting manager at once, resending recipes only once
* after all have been added.
*
* @apiNote This method differs from {@link #addRecipe} in that it will only resend the recipe book once instead of once per every recipe added
* @param recipes the recipes to add
* @return true if any recipe was added, false if none were for some reason
* @throws IllegalStateException if any of the recipes are already added to the crafting manager
* @see #addRecipe
*/
boolean addRecipes(@NotNull Iterable<Recipe> recipes);
/**
* Get a list of all recipes for a given item. The stack size is ignored
@@ -1205,6 +1224,14 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*/
public void resetRecipes();
/**
* Checks if the server has a recipe
*
* @param key NamespacedKey of recipe to check for
* @return True if there is a recipe with a matching key
*/
boolean hasRecipe(@NotNull NamespacedKey key);
/**
* Remove a recipe from the server.
*
@@ -1215,11 +1242,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
* @param key NamespacedKey of recipe to remove.
* @return True if recipe was removed
*/
default boolean removeRecipe(@NotNull NamespacedKey key) {
return this.removeRecipe(key, false);
}
boolean removeRecipe(@NotNull NamespacedKey key);
// Paper start - method to resend recipes
/**
* Remove a recipe from the server.
* <p>
@@ -1228,12 +1252,24 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
* players).</b>
*
* @param key NamespacedKey of recipe to remove.
* @param resendRecipes true to update all clients on the new recipe list.
* Will only update if a recipe was actually removed
* @param resendRecipes ignored, refer to the deprecation note
* @return True if recipe was removed
* @deprecated use {@link #removeRecipe(NamespacedKey)} instead, as Minecraft always resends recipes on recipe removal
*/
boolean removeRecipe(@NotNull NamespacedKey key, boolean resendRecipes);
// Paper end - method to resend recipes
@Deprecated(since = "26.3")
default boolean removeRecipe(@NotNull NamespacedKey key, boolean resendRecipes) {
return this.removeRecipe(key);
}
/**
* Remove multiple recipes from the server.
*
* @apiNote This method differs from {@link #removeRecipe} in that it will only resend the recipe book once instead of once per every recipe removed
* @param keys the NamespacedKeys of the recipes to remove
* @return true if any recipe was removed, false if none were for some reason
* @see #removeRecipe
*/
boolean removeRecipes(@NotNull Iterable<NamespacedKey> keys);
/**
* Gets a list of command aliases defined in the server properties.
@@ -1875,7 +1911,10 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*
* @param spawnCategory the category spawn
* @return the {@link SpawnCategory} spawn limit
* @deprecated this is configured per world; use {@link World#getSpawnLimit(SpawnCategory)}.
* This returns the primary world's value.
*/
@Deprecated(forRemoval = true, since = "26.3")
int getSpawnLimit(@NotNull SpawnCategory spawnCategory);
/**
@@ -2207,7 +2246,11 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
* Gets the active {@link org.bukkit.command.CommandMap}
*
* @return the active command map
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
@NotNull
org.bukkit.command.CommandMap getCommandMap();
@@ -2384,60 +2427,6 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
// Spigot start
public class Spigot {
/**
* @deprecated Server config options may be renamed or removed without notice. Prefer using existing API
* wherever possible, rather than directly reading from a server config.
*
* @see #getServerConfig()
* @return The server's spigot config.
*/
@Deprecated(since = "1.21.4", forRemoval = true)
@NotNull
public org.bukkit.configuration.file.YamlConfiguration getConfig() {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* @deprecated Server config options may be renamed or removed without notice. Prefer using existing API
* wherever possible, rather than directly reading from a server config.
*
* @see #getServerConfig()
* @return The server's bukkit config.
*/
// Paper start
@Deprecated(since = "1.21.4", forRemoval = true)
@NotNull
public org.bukkit.configuration.file.YamlConfiguration getBukkitConfig() {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* @deprecated Server config options may be renamed or removed without notice. Prefer using existing API
* wherever possible, rather than directly reading from a server config.
*
* @see #getServerConfig()
* @return The server's spigot config.
*/
@Deprecated(since = "1.21.4", forRemoval = true)
@NotNull
public org.bukkit.configuration.file.YamlConfiguration getSpigotConfig() {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* @deprecated Server config options may be renamed or removed without notice. Prefer using existing API
* wherever possible, rather than directly reading from a server config.
*
* @see #getServerConfig()
* @return The server's paper config.
*/
@Deprecated(since = "1.21.4", forRemoval = true)
@NotNull
public org.bukkit.configuration.file.YamlConfiguration getPaperConfig() {
throw new UnsupportedOperationException("Not supported yet.");
}
// Paper end
/**
* Sends the component to the player
*
@@ -2484,6 +2473,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*/
void restart();
@Deprecated(forRemoval = true, since = "26.3")
void reloadPermissions(); // Paper
boolean reloadCommandAliases(); // Paper
@@ -2504,7 +2494,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
* @deprecated use {@link #permissionMessage()}
*/
@NotNull
@Deprecated
@Deprecated(forRemoval = true)
String getPermissionMessage();
/**
+61 -7
View File
@@ -4,6 +4,7 @@ import io.papermc.paper.entity.poi.PoiSearchResult;
import io.papermc.paper.entity.poi.PoiType;
import io.papermc.paper.math.Position;
import io.papermc.paper.raytracing.PositionedRayTraceConfigurationBuilder;
import io.papermc.paper.world.chunk.ChunkGenerationState;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -12,6 +13,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.bukkit.block.Biome;
@@ -394,6 +396,58 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
*/
public boolean isChunkGenerated(int x, int z);
/**
* Checks the generation state of the {@link Chunk} at the specified coordinates
* without loading it, blocking until the state is known.
*
* <p>
* This may read chunk data from disk, which blocks the calling thread. Prefer
* {@link #getChunkGenerationStateAsync(int, int)} where possible.
* </p>
*
* <p>
* The result is a snapshot: the chunk may be generated, loaded, or saved
* concurrently, so the state may be stale by the time it is observed.
* </p>
*
* @param x X-coordinate of the chunk
* @param z Z-coordinate of the chunk
* @return the generation state of the chunk
* @throws RuntimeException if the chunk data cannot be read, or if it was saved
* by a newer version of the game
*/
@NotNull ChunkGenerationState getChunkGenerationState(int x, int z);
/**
* Checks the generation state of the {@link Chunk} at the specified coordinates
* without loading it.
*
* <p>
* The result is a snapshot: the chunk may be generated, loaded, or saved
* concurrently, so the state may be stale by the time it is observed.
* </p>
*
* <p>
* The returned future is completed on an unspecified thread. Callers that need to
* interact with the world should move to the appropriate thread themselves.
* </p>
*
* <p>
* If the main thread will block on the result, use {@link #getChunkGenerationState(int, int)}
* instead, which reads the chunk data at a priority suitable for blocking.
* </p>
*
* <p>
* The future is completed exceptionally if the chunk data cannot be read, or if
* it was saved by a newer version of the game.
* </p>
*
* @param x X-coordinate of the chunk
* @param z Z-coordinate of the chunk
* @return a future completed with the generation state of the chunk
*/
@NotNull CompletableFuture<@NotNull ChunkGenerationState> getChunkGenerationStateAsync(int x, int z);
/**
* Checks if the {@link Chunk} at the specified coordinates is loaded and
* in use by one or more players
@@ -3418,7 +3472,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* world
* <p>
* <b>Note:</b> If set to a negative number the world will use the
* server-wide spawn limit instead.
* spawn limit from its configuration instead.
*
* @param limit the new mob limit
* @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)}
@@ -3445,7 +3499,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* world
* <p>
* <b>Note:</b> If set to a negative number the world will use the
* server-wide spawn limit instead.
* spawn limit from its configuration instead.
*
* @param limit the new mob limit
* @deprecated Deprecated in favor of {@link #getSpawnLimit(SpawnCategory)}
@@ -3472,7 +3526,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* this world
* <p>
* <b>Note:</b> If set to a negative number the world will use the
* server-wide spawn limit instead.
* spawn limit from its configuration instead.
*
* @param limit the new mob limit
* @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)}
@@ -3499,7 +3553,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* this world
* <p>
* <b>Note:</b> If set to a negative number the world will use the
* server-wide spawn limit instead.
* spawn limit from its configuration instead.
*
* @param limit the new mob limit
* @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)}
@@ -3526,7 +3580,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* in this world
* <p>
* <b>Note:</b> If set to a negative number the world will use the
* server-wide spawn limit instead.
* spawn limit from its configuration instead.
*
* @param limit the new mob limit
* @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)}
@@ -3553,7 +3607,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* this world
* <p>
* <b>Note:</b> If set to a negative number the world will use the
* server-wide spawn limit instead.
* spawn limit from its configuration instead.
*
* @param limit the new mob limit
* @deprecated Deprecated in favor of {@link #setSpawnLimit(SpawnCategory, int)}
@@ -3577,7 +3631,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
* this world
* <p>
* <b>Note:</b> If set to a negative number the world will use the
* server-wide spawn limit instead.
* spawn limit from its configuration instead.
*
* @param spawnCategory the entity category
* @param limit the new mob limit
@@ -4,6 +4,7 @@ import java.util.Collection;
import java.util.function.Consumer;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Instrument;
import org.bukkit.Keyed;
import org.bukkit.Material;
import org.bukkit.Registry;
@@ -2986,4 +2987,12 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
* @return false if this block never has collision, true if it <b>might</b> have collision
*/
boolean hasCollision();
/**
* Gets the {@link Instrument} associated with this block.
* This determines the instrument sound that would be played if a Note Block were placed directly above or below this block.
*
* @return the associated instrument
*/
Instrument getInstrument();
}
@@ -0,0 +1,27 @@
package org.bukkit.block.data;
import org.bukkit.block.BlockFace;
import org.jspecify.annotations.NullMarked;
/**
* A marker interface for block data that is both {@link FaceAttachable} and {@link Directional}.
* <p>
* This represents blocks that are mounted on a specific face of another block (e.g., buttons, levers)
* and also have a facing direction.
*/
@NullMarked
public interface AttachableDirectional extends FaceAttachable, Directional {
/**
* Gets the {@link BlockFace} of the block that this block is physically attached to.
* This is determined by the attached face.
* @return the block face to which this block is attached
*/
default BlockFace getAttachedBlockFace() {
return switch (this.getAttachedFace()) {
case FLOOR -> BlockFace.DOWN;
case CEILING -> BlockFace.UP;
case WALL -> this.getFacing().getOppositeFace();
};
}
}
@@ -13,7 +13,6 @@ import org.bukkit.block.PistonMoveReaction;
import org.bukkit.block.structure.Mirror;
import org.bukkit.block.structure.StructureRotation;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -1,7 +1,6 @@
package org.bukkit.block.data.type;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.FaceAttachable;
import org.bukkit.block.data.AttachableDirectional;
public interface Grindstone extends Directional, FaceAttachable {
public interface Grindstone extends AttachableDirectional {
}
@@ -1,11 +1,10 @@
package org.bukkit.block.data.type;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.FaceAttachable;
import org.bukkit.block.data.AttachableDirectional;
import org.bukkit.block.data.Powerable;
import org.jetbrains.annotations.NotNull;
public interface Switch extends Directional, FaceAttachable, Powerable {
public interface Switch extends AttachableDirectional, Powerable {
@NotNull
AttachedFace getAttachedFace();
@@ -15,12 +15,18 @@ import org.bukkit.entity.minecart.CommandMinecart;
import org.bukkit.permissions.Permissible;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents a Command, which executes various tasks upon user input
*
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public abstract class Command {
private String name;
private String nextLabel;
@@ -1,10 +1,16 @@
package org.bukkit.command;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Represents a class which contains a single method for executing commands
*
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public interface CommandExecutor {
/**
@@ -18,6 +24,10 @@ public interface CommandExecutor {
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args);
}
@@ -2,9 +2,16 @@ package org.bukkit.command;
import java.util.List;
import org.bukkit.Location;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public interface CommandMap {
/**
@@ -7,8 +7,15 @@ import java.util.regex.Pattern; // Paper
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public class FormattedCommandAlias extends Command {
private final String[] formatStrings;
@@ -1,10 +1,16 @@
package org.bukkit.command;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Represents a command that delegates to one or more other commands
*
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public class MultipleCommandAlias extends Command {
private Command[] commands;
@@ -3,12 +3,18 @@ package org.bukkit.command;
import com.google.common.base.Preconditions;
import java.util.List;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents a {@link Command} belonging to a plugin
*
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public final class PluginCommand extends Command implements PluginIdentifiableCommand {
private final Plugin owningPlugin;
private CommandExecutor executor;
@@ -6,8 +6,15 @@ import java.util.Map;
import java.util.Map.Entry;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public class PluginCommandYamlParser {
@NotNull
@@ -1,6 +1,7 @@
package org.bukkit.command;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
@@ -8,7 +9,12 @@ import org.jetbrains.annotations.NotNull;
* sub-indexes based on the {@link Plugin} they are a part of. Custom command
* implementations will need to implement this interface to have a sub-index
* automatically generated on the plugin's behalf.
*
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public interface PluginIdentifiableCommand {
/**
@@ -16,9 +16,16 @@ import org.bukkit.command.defaults.HelpCommand;
import org.bukkit.command.defaults.ReloadCommand;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public class SimpleCommandMap implements CommandMap {
protected final Map<String, Command> knownCommands;
private final Server server;
@@ -1,12 +1,18 @@
package org.bukkit.command;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents a class which can suggest tab completions for commands.
*
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public interface TabCompleter {
/**
@@ -21,7 +27,11 @@ public interface TabCompleter {
* partial argument to be completed
* @return A List of possible completions for the final argument, or null
* to default to the command executor
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
@Nullable
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args);
}
@@ -1,8 +1,15 @@
package org.bukkit.command;
import org.jetbrains.annotations.ApiStatus;
/**
* This class is provided as a convenience to implement both TabCompleter and
* CommandExecutor.
*
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public interface TabExecutor extends TabCompleter, CommandExecutor {
}
@@ -2,8 +2,15 @@ package org.bukkit.command.defaults;
import java.util.List;
import org.bukkit.command.Command;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public abstract class BukkitCommand extends Command {
protected BukkitCommand(@NotNull String name) {
super(name);
@@ -1,18 +1,26 @@
package org.bukkit.enchantments;
import com.google.common.base.Preconditions;
import org.jetbrains.annotations.NotNull;
import io.papermc.paper.InternalAPIBridge;
import org.bukkit.inventory.ItemStack;
import java.util.Objects;
import org.checkerframework.checker.index.qual.Positive;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import static io.papermc.paper.util.BoundChecker.requirePositive;
/**
* A class for the available enchantment offers in the enchantment table.
*/
@NullMarked
public class EnchantmentOffer {
private Enchantment enchantment;
private int enchantmentLevel;
private int cost;
public EnchantmentOffer(@NotNull Enchantment enchantment, int enchantmentLevel, int cost) {
public EnchantmentOffer(final Enchantment enchantment, final int enchantmentLevel, final int cost) {
this.enchantment = enchantment;
this.enchantmentLevel = enchantmentLevel;
this.cost = cost;
@@ -23,9 +31,8 @@ public class EnchantmentOffer {
*
* @return type of enchantment
*/
@NotNull
public Enchantment getEnchantment() {
return enchantment;
return this.enchantment;
}
/**
@@ -33,7 +40,7 @@ public class EnchantmentOffer {
*
* @param enchantment type of the enchantment
*/
public void setEnchantment(@NotNull Enchantment enchantment) {
public void setEnchantment(final Enchantment enchantment) {
Preconditions.checkArgument(enchantment != null, "The enchantment may not be null!");
this.enchantment = enchantment;
@@ -45,7 +52,7 @@ public class EnchantmentOffer {
* @return level of the enchantment
*/
public int getEnchantmentLevel() {
return enchantmentLevel;
return this.enchantmentLevel;
}
/**
@@ -53,8 +60,8 @@ public class EnchantmentOffer {
*
* @param enchantmentLevel level of the enchantment
*/
public void setEnchantmentLevel(int enchantmentLevel) {
Preconditions.checkArgument(enchantmentLevel > 0, "The enchantment level must be greater than 0!");
public void setEnchantmentLevel(final @Positive int enchantmentLevel) {
requirePositive(enchantmentLevel, "enchantmentLevel");
this.enchantmentLevel = enchantmentLevel;
}
@@ -66,7 +73,7 @@ public class EnchantmentOffer {
* @return cost for this enchantment
*/
public int getCost() {
return cost;
return this.cost;
}
/**
@@ -75,9 +82,42 @@ public class EnchantmentOffer {
*
* @param cost cost for this enchantment
*/
public void setCost(int cost) {
Preconditions.checkArgument(cost > 0, "The cost must be greater than 0!");
public void setCost(final @Positive int cost) {
requirePositive(cost, "cost");
this.cost = cost;
}
@Override
public boolean equals(final @Nullable Object o) {
if (o == null || this.getClass() != o.getClass()) {
return false;
}
final EnchantmentOffer that = (EnchantmentOffer) o;
return this.getEnchantmentLevel() == that.getEnchantmentLevel() && this.getCost() == that.getCost() && Objects.equals(this.getEnchantment(), that.getEnchantment());
}
@Override
public int hashCode() {
return Objects.hash(this.getEnchantment(), this.getEnchantmentLevel(), this.getCost());
}
/**
* Rolls a new set of enchantment offers based on a target item stack, a seed and a bookshelf count.
*
* @param targetStack the itemstack for which the enchantment offers are rolled.
* @param seed the seed used for random selection of enchantments.
* Mirrors {@link org.bukkit.inventory.view.EnchantmentView#getEnchantmentSeed()}.
* @param bookshelfCount the number of virtual bookshelves to consider when rolling the enchantments.
* @param offerAmount the amount of enchantment offers to roll.
* @return an array of enchantment offers with size {@code offerAmount}.
*/
public static @Nullable EnchantmentOffer[] rollEnchantmentOffers(
final ItemStack targetStack,
final int seed,
final int bookshelfCount,
final int offerAmount
) {
return InternalAPIBridge.get().rollEnchantmentOffers(targetStack, seed, bookshelfCount, offerAmount);
}
}
@@ -173,8 +173,8 @@ public interface AbstractArrow extends Projectile {
*
* @return The firing ItemStack
*/
@Nullable // Paper
public ItemStack getWeapon();
@Override
@Nullable ItemStack getWeapon();
/**
* Sets the ItemStack which fired this arrow.
@@ -1,16 +1,20 @@
package org.bukkit.entity;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import io.papermc.paper.datacomponent.DataComponentView;
import io.papermc.paper.entity.LookAnchor;
import io.papermc.paper.entity.RemovalReason;
import io.papermc.paper.math.Angle;
import java.util.function.Predicate;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.Namespaced;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.event.HoverEventSource;
import net.kyori.adventure.util.TriState;
import org.bukkit.Chunk; // Paper
import org.bukkit.Chunk;
import org.bukkit.EntityEffect;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -22,6 +26,8 @@ import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.block.PistonMoveReaction;
import org.bukkit.command.CommandSender;
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageType;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityRemoveEvent;
@@ -36,6 +42,7 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView;
/**
* Represents a base entity in the world
@@ -152,7 +159,6 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
*/
void setRotation(@NotNull Angle yaw, @NotNull Angle pitch);
// Paper start - Teleport API
/**
* Teleports this entity to the given location.
* <p>
@@ -203,7 +209,6 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
default void lookAt(@NotNull io.papermc.paper.math.Position position, @NotNull LookAnchor entityAnchor) {
this.lookAt(position.x(), position.y(), position.z(), entityAnchor);
}
// Paper end - Teleport API
/**
* Teleports this entity to the given location.
@@ -259,7 +264,6 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
*/
public boolean teleport(@NotNull Entity destination, @NotNull TeleportCause cause);
// Paper start
/**
* Loads/Generates(in 1.13+) the Chunk asynchronously, and then teleports the entity when the chunk is ready.
* <p>
@@ -325,7 +329,6 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
* @return A future that will be completed with the result of the teleport
*/
java.util.concurrent.@NotNull CompletableFuture<Boolean> teleportAsync(@NotNull Location loc, @NotNull TeleportCause cause, @NotNull io.papermc.paper.entity.TeleportFlag @NotNull... teleportFlags);
// Paper end
/**
* Returns a list of entities within a bounding box centered around this
@@ -673,6 +676,17 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
@Nullable
public EntityDamageEvent getLastDamageCause();
/**
* Gets the item being used as a weapon by this entity.
* <p>
* Note: In most cases this is the item in the entity's main hand, but
* it may be the offhand if for ex: the entity is using the riptide
* enchantment on a trident.
*
* @return The weapon item
*/
@Nullable ItemStack getWeapon();
/**
* Returns a unique and persistent id for this entity
*
@@ -866,19 +880,44 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
/**
* Sets whether the entity is invulnerable or not.
* <p>
* When an entity is invulnerable it can only be damaged by players in
* When an entity is invulnerable, it can only be damaged by players in
* creative mode.
*
* @param flag if the entity is invulnerable
*/
public void setInvulnerable(boolean flag);
void setInvulnerable(boolean flag);
/**
* Gets whether the entity is invulnerable or not.
* Gets the current invulnerability state of the entity set by {@link #setInvulnerable(boolean)}.
*
* @return whether the entity is
* @return whether the entity is invulnerable
* @see #isInvulnerableTo(DamageSource)
*/
public boolean isInvulnerable();
boolean isMarkedInvulnerable();
/**
* Determines if the entity is invulnerable to the specified damage source.
* <p>
* Using this method with {@code DamageSource.builder(DamageType.GENERIC).build()} is equivalent to {@link #isInvulnerable()}
*
* @param source the damage source to check against
* @return {@code true} if the entity is invulnerable to the given damage source, {@code false} otherwise
*/
boolean isInvulnerableTo(@NotNull DamageSource source);
/**
* Gets whether the entity is invulnerable or not
*
* @return whether the entity is invulnerable
* @see #isInvulnerableTo(DamageSource)
* @deprecated This method does not reflect the invulnerability state set by
* {@link #setInvulnerable(boolean)}, use {@link #isMarkedInvulnerable()} for that instead.
* Use {@link #isInvulnerableTo(DamageSource)} with {@code DamageSource.builder(DamageType.GENERIC).build()} as a direct replacement to this method.
*/
@Deprecated(since = "26.3")
default boolean isInvulnerable() {
return this.isInvulnerableTo(DamageSource.builder(DamageType.GENERIC).build());
}
/**
* Gets whether the entity is silent or not.
@@ -910,6 +949,27 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
*/
void setGravity(boolean gravity);
/**
* Returns the default acceleration due to gravity (in blocks per tick). Ignores {@link Entity#hasGravity()}.
*
* @return the default acceleration due to gravity
*/
double getDefaultGravity();
/**
* Returns the acceleration due to gravity (in blocks per tick). If {@link Entity#hasGravity()} is false, returns 0.
*
* @return the acceleration due to gravity
*/
double getGravity();
/**
* Returns the air drag factor applied to this entity.
*
* @return the air drag factor
*/
float getAirDrag();
/**
* Gets the period of time (in ticks) before this entity can use a portal.
*
@@ -989,7 +1049,6 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
@NotNull
Pose getPose();
// Paper start
/**
* Returns if the entity is in sneak mode
*
@@ -1042,7 +1101,6 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
* @return whether the entity has a fixed {@link Pose}
*/
boolean hasFixedPose();
// Paper end
/**
* Get the category of spawn to which this entity belongs.
@@ -1102,7 +1160,6 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
@ApiStatus.Experimental
Entity copy(@NotNull Location to);
// Spigot start
public class Spigot extends CommandSender.Spigot {
}
@@ -1110,9 +1167,7 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
@NotNull
@Override
Spigot spigot();
// Spigot end
// Paper start
/**
* Gets the entity's display name formatted with their team prefix/suffix and
* the entity's default hover/click events.
@@ -1301,9 +1356,7 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
* @return the entity's yaw
*/
float getYaw();
// Paper end
// Paper start - Collision API
/**
* Checks for any collisions with the entity's bounding box at the provided location.
* This will check for any colliding entities (boats, shulkers) / worldborder / blocks.
@@ -1323,20 +1376,16 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
* @return collides or not
*/
boolean wouldCollideUsing(@NotNull BoundingBox boundingBox);
// Paper end - Collision API
// Paper start - Folia schedulers
/**
* Returns the task scheduler for this entity. The entity scheduler can be used to schedule tasks
* that are guaranteed to always execute on the tick thread that owns the entity.
* <p><b>If you do not need/want to make your plugin run on Folia, use {@link org.bukkit.Server#getScheduler()} instead.</b></p>
*
* @return the task scheduler for this entity.
* @see io.papermc.paper.threadedregions.scheduler.EntityScheduler
*/
@NotNull io.papermc.paper.threadedregions.scheduler.EntityScheduler getScheduler();
// Paper end - Folia schedulers
// Paper start - entity scoreboard name
/**
* Gets the string name of the entity used to track it in {@link org.bukkit.scoreboard.Scoreboard Scoreboards}.
*
@@ -1345,9 +1394,7 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
* @see org.bukkit.scoreboard.Scoreboard#getEntries()
*/
@NotNull String getScoreboardEntryName();
// Paper end - entity scoreboard name
// Paper start - broadcast hurt animation
/**
* Broadcasts a hurt animation. This fakes incoming damage towards the target entity.
* <p>
@@ -1358,5 +1405,49 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
* @throws IllegalArgumentException if {@code this} is contained in {@code players}
*/
void broadcastHurtAnimation(@NotNull java.util.Collection<Player> players);
// Paper end - broadcast hurt animation
/**
* Gets if the entity is allied to the given entity.
*
* @param other the other entity
* @return {@code true} if the entity is allied to the given entity
*/
boolean isAlliedTo(@NotNull Entity other);
/**
* Gets an immutable map of the current additional rules of alliance with any entity.
* <br>
* These are additional rules from the vanilla behavior for any entity, like share a team.
* <br>
* These rules are not (currently) persistent.
*
* @return an immutable map of the current additional rules of alliance with any entity
* @see #addAdditionalAlliedRule(Key, Predicate)
*/
@UnmodifiableView
@NotNull Map<Key, Predicate<Entity>> getAdditionalAlliedRules();
/**
* Add a rule of alliance with any entity.
* <br>
* If a rule with the specified {@link Key} already exists, it will be overwritten.
*
* @param key the key of the rule
* @param predicate the rule of alliance with any entity
*/
void addAdditionalAlliedRule(@NotNull Key key, @NotNull Predicate<Entity> predicate);
/**
* Remove the rule of alliance with any entity.
*
* @param key the key of the rule
*/
void removeAdditionalAlliedRule(@NotNull Key key);
/**
* Remove the rules of alliance with any entity.
*
* @param namespaced the namespaced of the rule
*/
void removeAdditionalAlliedRule(@NotNull Namespaced namespaced);
}
@@ -536,6 +536,17 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
*/
public void startRiptideAttack(int duration, float attackStrength, @Nullable ItemStack attackItem);
/**
* Check if the player can critical attack the target {@link Entity}.
* <br>
* Note: When hitting an entity, Minecraft also checks that the result of {@link #getAttackCooldown()} is {@code > 0.9F}
* to determine if the critical attack will happen.
*
* @param entity target entity to check if can critical attack
* @return whether they can critical attack the target entity
*/
public boolean canCriticalAttack(Entity entity);
/**
* Gets the location of the bed the player is currently sleeping in
*
@@ -981,7 +992,7 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
public int getStarvationRate();
/**
* Get the starvation rate (1 health per x ticks) of
* Set the starvation rate (1 health per x ticks) of
* the HumanEntity. Default is 80.
*
* @param ticks the amount of ticks to lose 1 health
@@ -33,9 +33,7 @@ public interface Mob extends LivingEntity, Lootable, Leashable {
* </ul>
*
* @param state a TriState representing the state of the override
* @deprecated the client no longer allows to change that setting and will not render the entity
*/
@Deprecated(since = "26.2")
void setDespawnInPeacefulOverride(TriState state);
/**
@@ -48,9 +46,7 @@ public interface Mob extends LivingEntity, Lootable, Leashable {
*
* @return a TriState representing the state of the override
* @see Mob#setDespawnInPeacefulOverride(TriState)
* @deprecated the client no longer allows to change that setting and will not render the entity
*/
@Deprecated(since = "26.2")
TriState getDespawnInPeacefulOverride();
@Override
@@ -10,6 +10,7 @@ import io.papermc.paper.math.Angle;
import io.papermc.paper.math.Position;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
@@ -275,7 +276,17 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* @return the player's address
*/
@Nullable
public InetSocketAddress getAddress();
SocketAddress getSocketAddress();
/**
* Returns an instance of {@link InetSocketAddress} associated with the
* player's socket address.
*
* @return the player's {@link InetSocketAddress}, or the loopback address
* if the player is connecting through a Unix domain socket
*/
@Nullable
InetSocketAddress getAddress();
// Paper start - Add API to get player's proxy address
/**
@@ -206,6 +206,13 @@ public class HandlerList {
return handlers;
}
/**
* {@return whether this handler list has any listeners}
*/
public boolean hasListeners() {
return this.getRegisteredListeners().length != 0;
}
/**
* Get a specific plugin's registered listeners associated with this
* handler list
@@ -1,6 +1,8 @@
package org.bukkit.event.player;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.UUID;
import com.destroystokyo.paper.profile.PlayerProfile;
import net.kyori.adventure.text.Component;
@@ -31,7 +33,7 @@ public class AsyncPlayerPreLoginEvent extends Event {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final InetAddress ipAddress;
private final InetAddress rawAddress;
private final SocketAddress rawAddress;
private final String hostname;
private final boolean transferred;
private Result result;
@@ -65,11 +67,11 @@ public class AsyncPlayerPreLoginEvent extends Event {
@ApiStatus.Internal
@Deprecated(forRemoval = true)
public AsyncPlayerPreLoginEvent(@NotNull final String name, @NotNull final InetAddress ipAddress, @NotNull final InetAddress rawAddress, @NotNull final UUID uniqueId, boolean transferred, @NotNull com.destroystokyo.paper.profile.PlayerProfile profile) {
this(name, ipAddress, rawAddress, uniqueId, transferred, profile, "", null);
this(name, ipAddress, new InetSocketAddress(rawAddress, 0), uniqueId, transferred, profile, "", null);
}
@ApiStatus.Internal
public AsyncPlayerPreLoginEvent(@NotNull final String name, @NotNull final InetAddress ipAddress, @NotNull final InetAddress rawAddress, @NotNull final UUID uniqueId, boolean transferred, @NotNull com.destroystokyo.paper.profile.PlayerProfile profile, @NotNull String hostname, final PlayerLoginConnection playerLoginConnection) {
public AsyncPlayerPreLoginEvent(@NotNull final String name, @NotNull final InetAddress ipAddress, @NotNull final SocketAddress rawAddress, @NotNull final UUID uniqueId, boolean transferred, @NotNull com.destroystokyo.paper.profile.PlayerProfile profile, @NotNull String hostname, final PlayerLoginConnection playerLoginConnection) {
super(true);
this.result = Result.ALLOWED;
this.message = Component.empty();
@@ -278,13 +280,24 @@ public class AsyncPlayerPreLoginEvent extends Event {
this.profile = profile;
}
/**
* Gets the raw socket address of the player logging in
* @return The socket address
*/
@NotNull
public SocketAddress getRawSocketAddress() {
return this.rawAddress;
}
/**
* Gets the raw address of the player logging in
* @return The address
* @return The address, or the loopback address if the player is connecting
* through a Unix domain socket
*/
@NotNull
public InetAddress getRawAddress() {
return this.rawAddress;
if (this.rawAddress instanceof InetSocketAddress inet) return inet.getAddress();
return InetAddress.getLoopbackAddress();
}
/**
@@ -1,6 +1,8 @@
package org.bukkit.event.player;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent;
import org.bukkit.Warning;
import net.kyori.adventure.text.Component;
@@ -32,18 +34,23 @@ public class PlayerLoginEvent extends PlayerEvent {
private final String hostname;
private final InetAddress address;
private final InetAddress realAddress;
private final SocketAddress realAddress;
private Result result = Result.ALLOWED;
private Component message = Component.empty();
@ApiStatus.Internal
public PlayerLoginEvent(@NotNull final Player player, @NotNull final String hostname, @NotNull final InetAddress address, final @NotNull InetAddress realAddress) {
public PlayerLoginEvent(@NotNull final Player player, @NotNull final String hostname, @NotNull final InetAddress address, final @NotNull SocketAddress realAddress) {
super(player);
this.hostname = hostname;
this.address = address;
this.realAddress = realAddress;
}
@ApiStatus.Internal
public PlayerLoginEvent(@NotNull final Player player, @NotNull final String hostname, @NotNull final InetAddress address, final @NotNull InetAddress realAddress) {
this(player, hostname, address, new InetSocketAddress(realAddress, 0));
}
@ApiStatus.Internal
public PlayerLoginEvent(@NotNull final Player player, @NotNull final String hostname, @NotNull final InetAddress address) {
this(player, hostname, address, address);
@@ -88,16 +95,30 @@ public class PlayerLoginEvent extends PlayerEvent {
return this.address;
}
/**
* Gets the connection socket address of this player, regardless of whether
* it has been spoofed or not.
*
* @return the player's connection socket address
*/
@NotNull
public SocketAddress getRealSocketAddress() {
return this.realAddress;
}
/**
* Gets the connection address of this player, regardless of whether it has
* been spoofed or not.
*
* @return the player's connection address
* @return the player's connection address, or the loopback address if the
* player is connecting through a Unix domain socket
* @see #getAddress()
*/
@NotNull
public InetAddress getRealAddress() {
return this.realAddress;
if (this.realAddress instanceof InetSocketAddress inet) return inet.getAddress();
return InetAddress.getLoopbackAddress();
}
/**
@@ -6,12 +6,12 @@ import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
/**
* Holds information for player movement events
*/
@NullMarked
public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
@@ -22,7 +22,7 @@ public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
private boolean cancelled;
@ApiStatus.Internal
public PlayerMoveEvent(@NotNull final Player player, @NotNull final Location from, @Nullable final Location to) {
public PlayerMoveEvent(final Player player, final Location from, final Location to) {
super(player);
this.from = from;
this.to = to;
@@ -33,7 +33,6 @@ public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
*
* @return Location the player moved from
*/
@NotNull
public Location getFrom() {
return this.from;
}
@@ -43,7 +42,7 @@ public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
*
* @param from New location to mark as the players previous location
*/
public void setFrom(@NotNull Location from) {
public void setFrom(Location from) {
this.validateLocation(from);
this.from = from.clone();
}
@@ -53,7 +52,6 @@ public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
*
* @return Location the player moved to
*/
@NotNull // Paper
public Location getTo() {
return to;
}
@@ -63,7 +61,7 @@ public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
*
* @param to New Location this player will move to
*/
public void setTo(@NotNull Location to) {
public void setTo(Location to) {
this.validateLocation(to);
this.to = to.clone();
}
@@ -137,18 +135,16 @@ public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
this.cancelled = cancel;
}
private void validateLocation(@NotNull Location loc) {
private void validateLocation(Location loc) {
Preconditions.checkArgument(loc != null, "Cannot use null location!");
Preconditions.checkArgument(loc.getWorld() != null, "Cannot use null location with null world!");
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
@@ -5,8 +5,7 @@ import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.util.Set;
/**
@@ -15,6 +14,7 @@ import java.util.Set;
* <p>
* For other entities see {@link org.bukkit.event.entity.EntityPortalEvent}
*/
@NullMarked
public class PlayerPortalEvent extends PlayerTeleportEvent {
private static final HandlerList HANDLER_LIST = new HandlerList();
@@ -24,17 +24,17 @@ public class PlayerPortalEvent extends PlayerTeleportEvent {
private int creationRadius = 16;
@ApiStatus.Internal
public PlayerPortalEvent(@NotNull final Player player, @NotNull final Location from, @Nullable final Location to) {
public PlayerPortalEvent(final Player player, final Location from, final Location to) {
super(player, from, to);
}
@ApiStatus.Internal
public PlayerPortalEvent(@NotNull Player player, @NotNull Location from, @Nullable Location to, @NotNull TeleportCause cause) {
public PlayerPortalEvent(Player player, Location from, Location to, TeleportCause cause) {
super(player, from, to, cause);
}
@ApiStatus.Internal
public PlayerPortalEvent(@NotNull Player player, @NotNull Location from, @Nullable Location to, @NotNull TeleportCause cause, int searchRadius, boolean canCreatePortal, int creationRadius) {
public PlayerPortalEvent(Player player, Location from, Location to, TeleportCause cause, int searchRadius, boolean canCreatePortal, int creationRadius) {
super(player, from, to, cause);
this.searchRadius = searchRadius;
this.canCreatePortal = canCreatePortal;
@@ -52,7 +52,7 @@ public class PlayerPortalEvent extends PlayerTeleportEvent {
* @return starting point for search or exact destination
*/
@Override
public @NotNull Location getTo() {
public Location getTo() {
return super.getTo();
}
@@ -62,7 +62,7 @@ public class PlayerPortalEvent extends PlayerTeleportEvent {
* @param to starting point for search or exact destination
*/
@Override
public void setTo(@NotNull final Location to) {
public void setTo(final Location to) {
super.setTo(to);
}
@@ -159,17 +159,15 @@ public class PlayerPortalEvent extends PlayerTeleportEvent {
*/
@Deprecated(forRemoval = true)
@Override
public @NotNull Set<TeleportFlag.Relative> getRelativeTeleportationFlags() {
public Set<TeleportFlag.Relative> getRelativeTeleportationFlags() {
return super.getRelativeTeleportationFlags();
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
@@ -7,15 +7,15 @@ import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
import java.util.Collections;
import java.util.Set;
/**
* Holds information for player teleport events
*/
@NullMarked
public class PlayerTeleportEvent extends PlayerMoveEvent {
private static final HandlerList HANDLER_LIST = new HandlerList();
@@ -24,19 +24,19 @@ public class PlayerTeleportEvent extends PlayerMoveEvent {
private TeleportCause cause = TeleportCause.UNKNOWN;
@ApiStatus.Internal
public PlayerTeleportEvent(@NotNull final Player player, @NotNull final Location from, @Nullable final Location to) {
public PlayerTeleportEvent(final Player player, final Location from, final Location to) {
super(player, from, to);
this.teleportFlags = Collections.emptySet();
}
@ApiStatus.Internal
public PlayerTeleportEvent(@NotNull final Player player, @NotNull final Location from, @Nullable final Location to, @NotNull final TeleportCause cause) {
public PlayerTeleportEvent(final Player player, final Location from, final Location to, final TeleportCause cause) {
this(player, from, to);
this.cause = cause;
}
@ApiStatus.Internal
public PlayerTeleportEvent(@NotNull final Player player, @NotNull final Location from, @Nullable final Location to, @NotNull final TeleportCause cause, @NotNull Set<TeleportFlag.Relative> teleportFlags) {
public PlayerTeleportEvent(final Player player, final Location from, final Location to, final TeleportCause cause, Set<TeleportFlag.Relative> teleportFlags) {
super(player, from, to);
this.cause = cause;
this.teleportFlags = teleportFlags;
@@ -47,7 +47,6 @@ public class PlayerTeleportEvent extends PlayerMoveEvent {
*
* @return Cause of the event
*/
@NotNull
public TeleportCause getCause() {
return this.cause;
}
@@ -58,7 +57,6 @@ public class PlayerTeleportEvent extends PlayerMoveEvent {
*
* @return an immutable set of relative teleportation flags
*/
@NotNull
public @Unmodifiable Set<TeleportFlag.Relative> getRelativeTeleportationFlags() {
return this.teleportFlags;
}
@@ -75,13 +73,11 @@ public class PlayerTeleportEvent extends PlayerMoveEvent {
return true;
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
@@ -2,6 +2,8 @@ package org.bukkit.event.server;
import com.google.common.base.Preconditions;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Iterator;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@@ -28,14 +30,14 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
private static final HandlerList HANDLER_LIST = new HandlerList();
private final String hostname;
private final InetAddress address;
private final SocketAddress address;
private final int numPlayers;
private Component motd;
private int maxPlayers;
@ApiStatus.Internal
@Deprecated(forRemoval = true)
public ServerListPingEvent(@NotNull final String hostname, @NotNull final InetAddress address, @NotNull final String motd, final int numPlayers, final int maxPlayers) {
public ServerListPingEvent(@NotNull final String hostname, @NotNull final SocketAddress address, @NotNull final String motd, final int numPlayers, final int maxPlayers) {
super(true);
Preconditions.checkArgument(numPlayers >= 0, "Cannot have negative number of players online", numPlayers);
this.hostname = hostname;
@@ -47,7 +49,7 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
@ApiStatus.Internal
@Deprecated(forRemoval = true)
protected ServerListPingEvent(@NotNull final String hostname, @NotNull final InetAddress address, @NotNull final String motd, final int maxPlayers) {
protected ServerListPingEvent(@NotNull final String hostname, @NotNull final SocketAddress address, @NotNull final String motd, final int maxPlayers) {
super(true);
this.numPlayers = MAGIC_PLAYER_COUNT;
this.hostname = hostname;
@@ -59,11 +61,11 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
@ApiStatus.Internal
@Deprecated(forRemoval = true)
public ServerListPingEvent(@NotNull final InetAddress address, @NotNull final Component motd, final int numPlayers, final int maxPlayers) {
this("", address, motd, numPlayers, maxPlayers);
this("", new InetSocketAddress(address, 0), motd, numPlayers, maxPlayers);
}
@ApiStatus.Internal
public ServerListPingEvent(@NotNull final String hostname, @NotNull final InetAddress address, @NotNull final Component motd, final int numPlayers, final int maxPlayers) {
public ServerListPingEvent(@NotNull final String hostname, @NotNull final SocketAddress address, @NotNull final Component motd, final int numPlayers, final int maxPlayers) {
super(true);
this.hostname = hostname;
this.address = address;
@@ -75,7 +77,7 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
@ApiStatus.Internal
@Deprecated(forRemoval = true)
protected ServerListPingEvent(@NotNull final InetAddress address, @NotNull final Component motd, final int maxPlayers) {
this("", address, motd, maxPlayers);
this("", new InetSocketAddress(address, 0), motd, maxPlayers);
}
/*
@@ -84,7 +86,7 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
* count.
*/
@ApiStatus.Internal
protected ServerListPingEvent(final @NotNull String hostname, final @NotNull InetAddress address, final @NotNull Component motd, final int maxPlayers) {
protected ServerListPingEvent(final @NotNull String hostname, final @NotNull SocketAddress address, final @NotNull Component motd, final int maxPlayers) {
this.numPlayers = MAGIC_PLAYER_COUNT;
this.hostname = hostname;
this.address = address;
@@ -103,14 +105,26 @@ public class ServerListPingEvent extends ServerEvent implements Iterable<Player>
return this.hostname;
}
/**
* Gets the socket address the ping is coming from.
*
* @return the socket address
*/
@NotNull
public SocketAddress getSocketAddress() {
return this.address;
}
/**
* Get the address the ping is coming from.
*
* @return the address
* @return the socket address, or the loopback address if the ping is
* coming from a Unix domain socket
*/
@NotNull
public InetAddress getAddress() {
return this.address;
if (this.address instanceof InetSocketAddress inet) return inet.getAddress();
return InetAddress.getLoopbackAddress();
}
/**
@@ -7,6 +7,7 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType;
import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -32,14 +33,14 @@ public interface Inventory extends Iterable<ItemStack> {
*
* @return The size of the inventory
*/
public int getSize();
int getSize();
/**
* Returns the maximum stack size for an ItemStack in this inventory.
*
* @return The maximum size for an ItemStack in this inventory.
*/
public int getMaxStackSize();
int getMaxStackSize();
/**
* This method allows you to change the maximum stack size for an
@@ -58,8 +59,9 @@ public interface Inventory extends Iterable<ItemStack> {
* </ul>
*
* @param size The new maximum stack size for items in this inventory.
* @throws IllegalArgumentException if size is negative
*/
public void setMaxStackSize(int size);
void setMaxStackSize(@NonNegative int size);
/**
* Returns the ItemStack found in the slot at the given index
@@ -1,5 +1,7 @@
package org.bukkit.persistence;
import com.google.common.base.Preconditions;
import io.papermc.paper.persistence.PersistentDataKey;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
@@ -26,11 +28,34 @@ public interface PersistentDataContainer extends io.papermc.paper.persistence.Pe
* @throws IllegalArgumentException if the key is null
* @throws IllegalArgumentException if the type is null
* @throws IllegalArgumentException if the value is null. Removing a tag should
* be done using {@link #remove(NamespacedKey)}
* be done using {@link #remove(NamespacedKey)} or {@link #remove(PersistentDataKey)}
* @throws IllegalArgumentException if no suitable adapter was found for
* the {@link PersistentDataType#getPrimitiveType()}
*/
<P, C> void set(@NotNull NamespacedKey key, @NotNull PersistentDataType<P, C> type, @NotNull C value);
/**
* Stores a metadata value on the {@link PersistentDataHolder} instance.
* <p>
* This API cannot be used to manipulate minecraft data, as the values will
* be stored using your namespace. This method will override any existing
* value the {@link PersistentDataHolder} may have stored under the provided
* key.
*
* @param dataKey the data key this value will be stored under
* @param value the value to store in the tag
* @param <C> the generic type of the object to store
*
* @throws IllegalArgumentException if the data key is null
* @throws IllegalArgumentException if the value is null. Removing a tag should
* be done using {@link #remove(NamespacedKey)} or {@link #remove(PersistentDataKey)}
* @throws IllegalArgumentException if no suitable adapter was found for
* the {@link PersistentDataType#getPrimitiveType()}
*/
default <C> void set(final @NotNull PersistentDataKey<C> dataKey, final @NotNull C value) {
Preconditions.checkArgument(dataKey != null, "The data key cannot be null");
this.set(dataKey.key(), dataKey.dataType(), value);
}
// Paper - move to PersistentDataContainerView
/**
@@ -41,6 +66,17 @@ public interface PersistentDataContainer extends io.papermc.paper.persistence.Pe
* @throws IllegalArgumentException if the provided key is null
*/
void remove(@NotNull NamespacedKey key);
/**
* Removes a custom key from the {@link PersistentDataHolder} instance.
*
* @param dataKey the data key to remove
* @throws IllegalArgumentException if the provided data key is null
*/
default void remove(final @NotNull PersistentDataKey<?> dataKey) {
Preconditions.checkArgument(dataKey != null, "The data key cannot be null");
this.remove(dataKey.key());
}
// Paper - move to PersistentDataContainerView
// Paper start - byte array serialization
@@ -31,6 +31,7 @@ import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.PluginBase;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@@ -309,16 +310,26 @@ public abstract class JavaPlugin extends PluginBase {
/**
* {@inheritDoc}
*
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@Override
@ApiStatus.Obsolete(since = "26.3")
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
return false;
}
/**
* {@inheritDoc}
*
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@Override
@ApiStatus.Obsolete(since = "26.3")
public @Nullable List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
return null;
}
@@ -332,7 +343,11 @@ public abstract class JavaPlugin extends PluginBase {
* @return the plugin command if found, otherwise null
* @throws UnsupportedOperationException if this plugin is a paper plugin and the method is called in {@link #onEnable()}
* @see #registerCommand(String, String, Collection, BasicCommand)
* @apiNote plugin developers should prefer to use the
* <a href="https://docs.papermc.io/paper/dev/command-api/basics/introduction/">Brigadier command API</a>.
* For a direct alternative to Bukkit commands, <a href="https://docs.papermc.io/paper/dev/command-api/misc/basic-command/">Basic commands</a> are recommended
*/
@ApiStatus.Obsolete(since = "26.3")
public @Nullable PluginCommand getCommand(String name) {
if (this.isBeingEnabled && !(pluginMeta instanceof PluginDescriptionFile)) {
throw new UnsupportedOperationException("""
+20 -6
View File
@@ -8,7 +8,7 @@ plugins {
`maven-publish`
idea
id("io.papermc.paperweight.core")
id("io.papermc.fill.gradle") version "1.0.12"
id("io.papermc.fill.gradle") version "1.0.14"
}
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
@@ -110,9 +110,12 @@ abstract class MockitoAgentProvider : CommandLineArgumentProvider {
dependencies {
implementation(project(":paper-api"))
implementation("ca.spottedleaf:leafpile:1.2.2")
implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+
implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21
implementation(platform("ca.spottedleaf.leafpile:bom:1.3.0"))
implementation("ca.spottedleaf.leafpile:common")
implementation("ca.spottedleaf.leafpile:concurrentutil")
implementation("ca.spottedleaf.leafpile:converter")
implementation("ca.spottedleaf.leafpile:ioutil")
implementation("org.jline:jline-terminal-ffm:4.4.6")
implementation("net.minecrell:terminalconsoleappender:1.3.0")
implementation("net.kyori:adventure-text-serializer-ansi")
@@ -133,7 +136,7 @@ dependencies {
implementation("io.netty:netty-codec-haproxy:4.2.16.Final") // Add support for proxy protocol
implementation("org.apache.logging.log4j:log4j-iostreams:2.26.0")
implementation("org.ow2.asm:asm-commons:9.9.1")
implementation("org.spongepowered:configurate-yaml:4.2.0")
implementation("org.spongepowered:configurate-core:4.2.0") // plus GeyserMC's yaml loader with comments in io.papermc.paper.configuration.yaml
// Deps that were previously in the API but have now been moved here for backwards compat, eventually to be removed
runtimeOnly("commons-lang:commons-lang:2.6")
@@ -156,7 +159,7 @@ dependencies {
// Spark
implementation("me.lucko:spark-api:0.1-20240720.200737-2")
implementation("me.lucko:spark-paper:1.10.180")
implementation("me.lucko:spark-paper:1.10.191")
}
tasks.jar {
@@ -244,6 +247,16 @@ tasks.test {
val provider = objects.newInstance<MockitoAgentProvider>()
provider.fileCollection.from(mockitoAgent)
jvmArgumentProviders.add(provider)
// Hides repeated jline/joml warnings due to forking
jvmArgs("--enable-native-access=ALL-UNNAMED")
systemProperty("joml.nounsafe", true)
}
tasks.withType<io.papermc.paperweight.tasks.CreateBundlerJar>().configureEach {
extraManifestMainAttributes = mapOf(
"Enable-Native-Access" to "ALL-UNNAMED",
)
}
val generatedDir: java.nio.file.Path = layout.projectDirectory.dir("src/generated/java").asFile.toPath()
@@ -292,6 +305,7 @@ fun TaskContainer.registerRunTask(
systemProperty("paper.alwaysPrintWarningState", true)
systemProperty("paper.maxChatCommandInputSize", 32767)
systemProperty("paper.disableMigrationDelay", true)
systemProperty("joml.nounsafe", true)
systemProperty("paper.updatingMinecraft", providers.gradleProperty("updatingMinecraft").getOrElse("false").toBoolean())
val memoryGb = providers.gradleProperty("paper.runMemoryGb").getOrElse("2")
@@ -5,10 +5,10 @@ Subject: [PATCH] Add explicit flush support to Log4j AsyncAppender
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
index 639ff94cd3cdd350a0ce7bee48560baedaa3c8c3..fbad2fd2ea324fec44f27ac35024579b5436db25 100644
index 630de708dcd41fc9f75bdf55cc09d1252e11bd95..b711c1fba85d866a2cae702e26942744da5d744e 100644
--- a/io/papermc/paper/FeatureHooks.java
+++ b/io/papermc/paper/FeatureHooks.java
@@ -246,6 +246,20 @@ public final class FeatureHooks {
@@ -254,6 +254,20 @@ public final class FeatureHooks {
}
public static void flushAsyncAppenders() {
@@ -8,10 +8,10 @@ for details.
diff --git a/ca/spottedleaf/dataconverter/minecraft/MCDataConverter.java b/ca/spottedleaf/dataconverter/minecraft/MCDataConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..45da8ee6046fa5e3dfa88dd90a112b11aa60dc6c
index 0000000000000000000000000000000000000000..f664ad3c770bc2303604d2ea727eacb58a49249f
--- /dev/null
+++ b/ca/spottedleaf/dataconverter/minecraft/MCDataConverter.java
@@ -0,0 +1,86 @@
@@ -0,0 +1,137 @@
+package ca.spottedleaf.dataconverter.minecraft;
+
+import ca.spottedleaf.converter.DataConverter;
@@ -21,12 +21,22 @@ index 0000000000000000000000000000000000000000..45da8ee6046fa5e3dfa88dd90a112b11
+import ca.spottedleaf.dataconverter.types.json.JsonMapType;
+import ca.spottedleaf.dataconverter.types.nbt.NBTMapType;
+import com.google.gson.JsonObject;
+import com.mojang.datafixers.DSL;
+import com.mojang.datafixers.DataFixer;
+import com.mojang.logging.LogUtils;
+import com.mojang.serialization.Dynamic;
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtOps;
+import net.minecraft.util.datafix.DataFixTypes;
+import net.minecraft.util.datafix.DataFixers;
+import org.slf4j.Logger;
+
+public final class MCDataConverter {
+
+ private static final LongArrayList BREAKPOINTS = MCVersionRegistry.getBreakpoints();
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private static boolean warnedForPassOn;
+
+ public static <T> T copy(final T type) {
+ if (type instanceof CompoundTag) {
@@ -38,6 +48,47 @@ index 0000000000000000000000000000000000000000..45da8ee6046fa5e3dfa88dd90a112b11
+ return type;
+ }
+
+ public static CompoundTag convertTag(final MCDataType type, final DataFixTypes vanillaType, final CompoundTag data, final int fromVersion, final int toVersion) {
+ final int maxVersion = MCVersionRegistry.getMaxImplementedVersion();
+ if (toVersion > maxVersion) {
+ warnPassOn(toVersion);
+
+ // Convert what we can first
+ final CompoundTag converted = fromVersion < maxVersion ? convertTag(type, data, fromVersion, maxVersion) : data;
+
+ // Convert the rest via Vanilla fixers
+ return vanillaType.update(DataFixers.getDataFixer(), converted, Math.max(fromVersion, maxVersion), toVersion);
+ }
+
+ return convertTag(type, data, fromVersion, toVersion);
+ }
+
+ public static CompoundTag convertTag(final MCDataType type, final DSL.TypeReference vanillaType, final DataFixer fixer, final CompoundTag data, final int fromVersion, final int toVersion) {
+ final int maxVersion = MCVersionRegistry.getMaxImplementedVersion();
+ if (toVersion > maxVersion) {
+ warnPassOn(toVersion);
+
+ // Convert what we can first
+ final CompoundTag converted = fromVersion < maxVersion ? convertTag(type, data, fromVersion, maxVersion) : data;
+
+ // Convert the rest via Vanilla fixers
+ return (CompoundTag)fixer.update(
+ vanillaType,
+ new Dynamic<>(NbtOps.INSTANCE, converted),
+ Math.max(fromVersion, maxVersion), toVersion
+ ).getValue();
+ }
+
+ return convertTag(type, data, fromVersion, toVersion);
+ }
+
+ private static void warnPassOn(final int toVersion) {
+ if (!warnedForPassOn) {
+ warnedForPassOn = true;
+ LOGGER.warn("Passing on conversion to Vanilla converters to version: {}", toVersion);
+ }
+ }
+
+ public static CompoundTag convertTag(final MCDataType type, final CompoundTag data, final int fromVersion, final int toVersion) {
+ final NBTMapType wrapped = new NBTMapType(data);
+
@@ -100,10 +151,10 @@ index 0000000000000000000000000000000000000000..45da8ee6046fa5e3dfa88dd90a112b11
+}
diff --git a/ca/spottedleaf/dataconverter/minecraft/MCVersionRegistry.java b/ca/spottedleaf/dataconverter/minecraft/MCVersionRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..65514ce5def43ac1a13cf8b0e78276793a481ebf
index 0000000000000000000000000000000000000000..ea79ce7a0c8315f474aca002ed1449068165101e
--- /dev/null
+++ b/ca/spottedleaf/dataconverter/minecraft/MCVersionRegistry.java
@@ -0,0 +1,533 @@
@@ -0,0 +1,541 @@
+package ca.spottedleaf.dataconverter.minecraft;
+
+import ca.spottedleaf.converter.DataConverter;
@@ -134,6 +185,10 @@ index 0000000000000000000000000000000000000000..65514ce5def43ac1a13cf8b0e7827679
+ private static final LongLinkedOpenHashSet DATACONVERTER_VERSIONS = new LongLinkedOpenHashSet();
+ private static final Int2ObjectLinkedOpenHashMap<IntArrayList> SUBVERSIONS = new Int2ObjectLinkedOpenHashMap<>();
+ private static final LongArrayList BREAKPOINTS = new LongArrayList();
+
+ // Anything above this version will pass conversion through Vanilla fixers instead
+ private static final int MAX_IMPLEMENTED_VERSION = MCVersions.V26_3;
+
+ static {
+ // Note: Some of these are nameless.
+ // Unless a data version is specified here, it will NOT have converters ran for it. Please add them on update!
@@ -625,6 +680,10 @@ index 0000000000000000000000000000000000000000..65514ce5def43ac1a13cf8b0e7827679
+ return VERSION_LIST.getInt(VERSION_LIST.size() - 1);
+ }
+
+ public static int getMaxImplementedVersion() {
+ return MAX_IMPLEMENTED_VERSION;
+ }
+
+ public static LongArrayList getBreakpoints() {
+ return BREAKPOINTS;
+ }
@@ -5942,6 +6001,56 @@ index 0000000000000000000000000000000000000000..bcc586cb68148fd960dd685eecce8531
+ return ID_TO_STRING[id & 255];
+ }
+}
diff --git a/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperTooltipDisplay.java b/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperTooltipDisplay.java
new file mode 100644
index 0000000000000000000000000000000000000000..18675aeb5740e3683ba5f9e234592ea1a2b9b201
--- /dev/null
+++ b/ca/spottedleaf/dataconverter/minecraft/converters/helpers/HelperTooltipDisplay.java
@@ -0,0 +1,44 @@
+package ca.spottedleaf.dataconverter.minecraft.converters.helpers;
+
+import ca.spottedleaf.converter.types.ListType;
+import ca.spottedleaf.converter.types.MapType;
+import ca.spottedleaf.converter.types.ObjectType;
+import ca.spottedleaf.dataconverter.util.NamespaceUtil;
+
+public final class HelperTooltipDisplay {
+
+ public static void replaceHiddenComponent(final MapType components, final String from, final String... to) {
+ final MapType tooltipDisplay = components.getMap("minecraft:tooltip_display");
+ if (tooltipDisplay == null) {
+ return;
+ }
+
+ final ListType hiddenComponents = tooltipDisplay.getList("hidden_components", ObjectType.STRING);
+ if (hiddenComponents == null) {
+ return;
+ }
+
+ int index = -1;
+ for (int i = hiddenComponents.size() - 1; i >= 0; --i) {
+ if (from.equals(NamespaceUtil.correctNamespace(hiddenComponents.getString(i, "")))) {
+ hiddenComponents.remove(i);
+ index = i;
+ }
+ }
+ if (index == -1) {
+ return;
+ }
+
+ add_loop:
+ for (final String component : to) {
+ for (int i = 0, len = hiddenComponents.size(); i < len; ++i) {
+ if (component.equals(NamespaceUtil.correctNamespace(hiddenComponents.getString(i, "")))) {
+ continue add_loop;
+ }
+ }
+ hiddenComponents.addString(index++, component);
+ }
+ }
+
+ private HelperTooltipDisplay() {}
+}
diff --git a/ca/spottedleaf/dataconverter/minecraft/converters/itemname/ConverterAbstractItemRename.java b/ca/spottedleaf/dataconverter/minecraft/converters/itemname/ConverterAbstractItemRename.java
new file mode 100644
index 0000000000000000000000000000000000000000..94569f0ccff0d3a09eafd4ba73572d9db0a0ac5b
@@ -18103,7 +18212,7 @@ index 0000000000000000000000000000000000000000..e6a2f29b20aa6d7cd431fc63c2d8ed70
+}
diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V2701.java b/ca/spottedleaf/dataconverter/minecraft/versions/V2701.java
new file mode 100644
index 0000000000000000000000000000000000000000..f9111654dd0fba78c119c6c9ebc5c6f72096b3c2
index 0000000000000000000000000000000000000000..2c4cc929984e03ddd509bea4ad5643a9b9cbb0ec
--- /dev/null
+++ b/ca/spottedleaf/dataconverter/minecraft/versions/V2701.java
@@ -0,0 +1,205 @@
@@ -18157,12 +18266,12 @@ index 0000000000000000000000000000000000000000..f9111654dd0fba78c119c6c9ebc5c6f7
+ continue;
+ }
+
+ final String poolElement = child.getString("pool_element");
+ if (!"minecraft:feature_pool_element".equals(poolElement)) {
+ final MapType poolElement = child.getMap("pool_element");
+ if (poolElement == null || !"minecraft:feature_pool_element".equals(poolElement.getString("element_type"))) {
+ continue;
+ }
+
+ final MapType feature = child.getMap("feature");
+ final MapType feature = poolElement.getMap("feature");
+ if (feature == null) {
+ continue;
+ }
@@ -18170,7 +18279,7 @@ index 0000000000000000000000000000000000000000..f9111654dd0fba78c119c6c9ebc5c6f7
+ final String replacement = convertToString(feature);
+
+ if (replacement != null) {
+ child.setString("feature", replacement);
+ poolElement.setString("feature", replacement);
+ }
+ }
+
@@ -28028,17 +28137,22 @@ index 0000000000000000000000000000000000000000..eb2f1f913fbc02aa3e01f36d11ddf8fc
+}
diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V5006.java b/ca/spottedleaf/dataconverter/minecraft/versions/V5006.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3443c948c59bd1808667603642f9eb126e447be
index 0000000000000000000000000000000000000000..2ee438b5b8bed4cf6d7bdaa110206f0d4f148fc2
--- /dev/null
+++ b/ca/spottedleaf/dataconverter/minecraft/versions/V5006.java
@@ -0,0 +1,32 @@
@@ -0,0 +1,148 @@
+package ca.spottedleaf.dataconverter.minecraft.versions;
+
+import ca.spottedleaf.converter.DataConverter;
+import ca.spottedleaf.converter.datatypes.DataWalker;
+import ca.spottedleaf.converter.types.ListType;
+import ca.spottedleaf.converter.types.MapType;
+import ca.spottedleaf.converter.types.ObjectType;
+import ca.spottedleaf.converter.util.RenameHelper;
+import ca.spottedleaf.dataconverter.minecraft.MCVersions;
+import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
+import ca.spottedleaf.dataconverter.minecraft.walkers.generic.WalkerUtils;
+import ca.spottedleaf.dataconverter.util.NamespaceUtil;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
@@ -28060,22 +28174,134 @@ index 0000000000000000000000000000000000000000..e3443c948c59bd1808667603642f9eb1
+ return null;
+ }
+ });
+
+ // previous version: 1466
+ MCTypeRegistry.STRUCTURE_FEATURE.addStructureWalker(VERSION, new DataWalker<>() {
+ @Override
+ public @Nullable MapType walk(final MapType data, final long fromVersion, final long toVersion) {
+ final ListType list = data.getList("Children", ObjectType.MAP);
+ if (list != null) {
+ for (int i = 0, len = list.size(); i < len; ++i) {
+ final MapType child = list.getMap(i);
+
+ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CA", fromVersion, toVersion);
+ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CB", fromVersion, toVersion);
+ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CC", fromVersion, toVersion);
+ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, child, "CD", fromVersion, toVersion);
+
+ walkPoolElement(child.getMap("pool_element"), fromVersion, toVersion);
+ }
+ }
+
+ WalkerUtils.convert(MCTypeRegistry.BIOME, data, "biome", fromVersion, toVersion);
+
+ return null;
+ }
+ });
+ }
+
+ private static void walkPoolElement(final @Nullable MapType element, final long fromVersion, final long toVersion) {
+ if (element == null) {
+ return;
+ }
+
+ final ListType elements = element.getList("elements", ObjectType.MAP);
+ if (elements != null) {
+ for (int i = 0, len = elements.size(); i < len; ++i) {
+ walkPoolElement(elements.getMap(i), fromVersion, toVersion);
+ }
+ }
+
+ final Object processors = element.getGeneric("processors");
+ if (processors instanceof ListType list) {
+ walkProcessors(list, fromVersion, toVersion);
+ } else if (processors instanceof MapType map) {
+ walkProcessors(map.getList("processors", ObjectType.MAP), fromVersion, toVersion);
+ }
+ }
+
+ private static void walkProcessors(final @Nullable ListType processors, final long fromVersion, final long toVersion) {
+ if (processors == null) {
+ return;
+ }
+
+ for (int i = 0, len = processors.size(); i < len; ++i) {
+ walkProcessor(processors.getMap(i, null), fromVersion, toVersion);
+ }
+ }
+
+ private static void walkProcessor(final @Nullable MapType processor, final long fromVersion, final long toVersion) {
+ if (processor == null) {
+ return;
+ }
+
+ switch (NamespaceUtil.correctNamespace(processor.getString("processor_type", ""))) {
+ case "minecraft:rule": {
+ final ListType rules = processor.getList("rules", ObjectType.MAP);
+ if (rules == null) {
+ break;
+ }
+
+ for (int i = 0, len = rules.size(); i < len; ++i) {
+ final MapType rule = rules.getMap(i);
+
+ walkRuleTest(rule.getMap("input_predicate"), fromVersion, toVersion);
+ walkRuleTest(rule.getMap("location_predicate"), fromVersion, toVersion);
+ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, rule, "output_state", fromVersion, toVersion);
+ }
+ break;
+ }
+ case "minecraft:block_ignore": {
+ WalkerUtils.convertList(MCTypeRegistry.BLOCK_STATE, processor, "blocks", fromVersion, toVersion);
+ break;
+ }
+ case "minecraft:capped": {
+ walkProcessor(processor.getMap("delegate"), fromVersion, toVersion);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+
+ private static void walkRuleTest(final @Nullable MapType test, final long fromVersion, final long toVersion) {
+ if (test == null) {
+ return;
+ }
+
+ switch (NamespaceUtil.correctNamespace(test.getString("predicate_type", ""))) {
+ case "minecraft:block_match":
+ case "minecraft:random_block_match": {
+ WalkerUtils.convert(MCTypeRegistry.BLOCK_NAME, test, "block", fromVersion, toVersion);
+ break;
+ }
+ case "minecraft:blockstate_match":
+ case "minecraft:random_blockstate_match": {
+ WalkerUtils.convert(MCTypeRegistry.BLOCK_STATE, test, "block_state", fromVersion, toVersion);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+
+ private V5006() {}
+}
diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V5007.java b/ca/spottedleaf/dataconverter/minecraft/versions/V5007.java
new file mode 100644
index 0000000000000000000000000000000000000000..94eb076321058d12940ccc6bb31170ce475227c8
index 0000000000000000000000000000000000000000..a89c8fb5cdb5e371d7762def0b450a169b1b0bfd
--- /dev/null
+++ b/ca/spottedleaf/dataconverter/minecraft/versions/V5007.java
@@ -0,0 +1,32 @@
@@ -0,0 +1,34 @@
+package ca.spottedleaf.dataconverter.minecraft.versions;
+
+import ca.spottedleaf.converter.DataConverter;
+import ca.spottedleaf.converter.types.ListType;
+import ca.spottedleaf.converter.types.MapType;
+import ca.spottedleaf.dataconverter.minecraft.MCVersions;
+import ca.spottedleaf.dataconverter.minecraft.converters.helpers.HelperTooltipDisplay;
+import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
@@ -28095,6 +28321,7 @@ index 0000000000000000000000000000000000000000..94eb076321058d12940ccc6bb31170ce
+ data.setGeneric("minecraft:attack_animation", animation);
+ data.setGeneric("minecraft:interact_animation", animation instanceof MapType map ? map.copy() : animation instanceof ListType list ? list.copy() : animation);
+ }
+ HelperTooltipDisplay.replaceHiddenComponent(data, "minecraft:swing_animation", "minecraft:attack_animation", "minecraft:interact_animation");
+ return null;
+ }
+ });
@@ -28104,15 +28331,16 @@ index 0000000000000000000000000000000000000000..94eb076321058d12940ccc6bb31170ce
+}
diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V5008.java b/ca/spottedleaf/dataconverter/minecraft/versions/V5008.java
new file mode 100644
index 0000000000000000000000000000000000000000..f470dd94811baf54a41ab33d98ee4219a924e0b5
index 0000000000000000000000000000000000000000..0b849cfda70d804b899afe4adac89deb59ce1d64
--- /dev/null
+++ b/ca/spottedleaf/dataconverter/minecraft/versions/V5008.java
@@ -0,0 +1,81 @@
@@ -0,0 +1,83 @@
+package ca.spottedleaf.dataconverter.minecraft.versions;
+
+import ca.spottedleaf.converter.DataConverter;
+import ca.spottedleaf.converter.types.MapType;
+import ca.spottedleaf.dataconverter.minecraft.MCVersions;
+import ca.spottedleaf.dataconverter.minecraft.converters.helpers.HelperTooltipDisplay;
+import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
+import ca.spottedleaf.dataconverter.util.NamespaceUtil;
+import java.util.Map;
@@ -28182,6 +28410,7 @@ index 0000000000000000000000000000000000000000..f470dd94811baf54a41ab33d98ee4219
+ @Override
+ public @Nullable MapType convert(final MapType data, final long sourceVersion, final long toVersion) {
+ data.remove("minecraft:map_color");
+ HelperTooltipDisplay.replaceHiddenComponent(data, "minecraft:map_color");
+ return null;
+ }
+ });
@@ -28377,15 +28606,16 @@ index 0000000000000000000000000000000000000000..3eec6b425a15bef57b5e7640bc1fd5e0
+}
diff --git a/ca/spottedleaf/dataconverter/minecraft/versions/V5014.java b/ca/spottedleaf/dataconverter/minecraft/versions/V5014.java
new file mode 100644
index 0000000000000000000000000000000000000000..571eb563b65ec79f57c8e9af43fbe3e805ce8ac3
index 0000000000000000000000000000000000000000..8bb7030f5c6e0ba7d248b52af6ff7c5489cb6ed1
--- /dev/null
+++ b/ca/spottedleaf/dataconverter/minecraft/versions/V5014.java
@@ -0,0 +1,26 @@
@@ -0,0 +1,28 @@
+package ca.spottedleaf.dataconverter.minecraft.versions;
+
+import ca.spottedleaf.converter.DataConverter;
+import ca.spottedleaf.converter.types.MapType;
+import ca.spottedleaf.dataconverter.minecraft.MCVersions;
+import ca.spottedleaf.dataconverter.minecraft.converters.helpers.HelperTooltipDisplay;
+import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
@@ -28400,6 +28630,7 @@ index 0000000000000000000000000000000000000000..571eb563b65ec79f57c8e9af43fbe3e8
+ @Override
+ public @Nullable MapType convert(final MapType data, final long sourceVersion, final long toVersion) {
+ data.remove("minecraft:block_transformer");
+ HelperTooltipDisplay.replaceHiddenComponent(data, "minecraft:block_transformer");
+ return null;
+ }
+ });
@@ -33555,7 +33786,7 @@ index 0000000000000000000000000000000000000000..4ffd23c6ae2201fed0f1be719ef838c9
+}
diff --git a/ca/spottedleaf/dataconverter/util/ConvertUtil.java b/ca/spottedleaf/dataconverter/util/ConvertUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..ffce10dd0f244c05e437422f75cf6c29b3ebcd70
index 0000000000000000000000000000000000000000..04ba6d6c54fde178f086c35c7aa7c3d62aa5fc84
--- /dev/null
+++ b/ca/spottedleaf/dataconverter/util/ConvertUtil.java
@@ -0,0 +1,214 @@
@@ -33731,7 +33962,7 @@ index 0000000000000000000000000000000000000000..ffce10dd0f244c05e437422f75cf6c29
+ if (USE_DATACONVERTER) {
+ final MCDataType mcDataType = getDCType(type);
+
+ return MCDataConverter.convertTag(mcDataType, input, fromVersion, toVersion);
+ return MCDataConverter.convertTag(mcDataType, type, fixer, input, fromVersion, toVersion);
+ } else {
+ return (CompoundTag)fixer.update(
+ type, new Dynamic<>(NbtOps.INSTANCE, input), fromVersion, toVersion
@@ -33819,7 +34050,7 @@ index 0000000000000000000000000000000000000000..863a9785c84af6e0c961ae32f0102b5d
+ }
+}
diff --git a/ca/spottedleaf/moonrise/paper/PaperHooks.java b/ca/spottedleaf/moonrise/paper/PaperHooks.java
index 7bb2210f2fea91b3f611a58d39a57279200abeb0..4a62c50d0b4fe82c3aacaa56b303b9699120c778 100644
index 7050fd6efb6fec16ba5657e254c54de1c671c9a0..aa14dd369d5d2c5c843839f289afdc39c200f0c3 100644
--- a/ca/spottedleaf/moonrise/paper/PaperHooks.java
+++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java
@@ -216,6 +216,11 @@ public final class PaperHooks extends BaseChunkSystemHooks implements PlatformHo
@@ -33842,7 +34073,7 @@ index 7bb2210f2fea91b3f611a58d39a57279200abeb0..4a62c50d0b4fe82c3aacaa56b303b969
\ No newline at end of file
+}
diff --git a/net/minecraft/data/structures/StructureUpdater.java b/net/minecraft/data/structures/StructureUpdater.java
index 06103ffdf599af91bc1d3baf89772a1acc93a21a..ddaa6ba67a2af00565fe9cd0dbeafcac0ff041ec 100644
index 06103ffdf599af91bc1d3baf89772a1acc93a21a..fc1f82d313acb8bf9bd03b1f09c680355aabc886 100644
--- a/net/minecraft/data/structures/StructureUpdater.java
+++ b/net/minecraft/data/structures/StructureUpdater.java
@@ -27,7 +27,7 @@ public class StructureUpdater implements SnbtToNbt.Filter {
@@ -33850,7 +34081,7 @@ index 06103ffdf599af91bc1d3baf89772a1acc93a21a..ddaa6ba67a2af00565fe9cd0dbeafcac
}
- CompoundTag updated = DataFixTypes.STRUCTURE.updateToCurrentVersion(DataFixers.getDataFixer(), tag, fromVersion);
+ CompoundTag updated = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.STRUCTURE, tag, fromVersion, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); // Paper - rewrite dataconverter system
+ CompoundTag updated = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.STRUCTURE, DataFixTypes.STRUCTURE, tag, fromVersion, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); // Paper - rewrite dataconverter system
structureTemplate.load(BuiltInRegistries.BLOCK, updated);
return structureTemplate.save(new CompoundTag());
}
@@ -33867,7 +34098,7 @@ index d40863262dd7cf0e7c5667fd836e80d4f47e25aa..c4adc7266f41057bc93e8c34929c079c
Thread thread = new ca.spottedleaf.moonrise.common.util.TickThread(() -> serverReference.get().runServer(), "Server thread");
thread.setUncaughtExceptionHandler((t, e) -> LOGGER.error("Uncaught exception in server thread", e));
diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
index ebb94d87e3b659e9049cbe5a3d5375274773fe57..4257d11f9d046249fd78fc8f453bb7c59818dd23 100644
index ebb94d87e3b659e9049cbe5a3d5375274773fe57..2ccb20f4fa0a2ce514b199616cbd791fd749b3f8 100644
--- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
+++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
@@ -79,7 +79,7 @@ public class SimpleRegionStorage implements AutoCloseable {
@@ -33875,7 +34106,7 @@ index ebb94d87e3b659e9049cbe5a3d5375274773fe57..4257d11f9d046249fd78fc8f453bb7c5
// Spigot end
injectDatafixingContext(chunkTag, dataFixContextTag);
- chunkTag = this.dataFixType.update(this.fixerUpper, chunkTag, version, targetVersion);
+ chunkTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(this.getDataConverterType(), chunkTag, version, targetVersion); // Paper - rewrite dataconverter system
+ chunkTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(this.getDataConverterType(), this.dataFixType, chunkTag, version, targetVersion); // Paper - rewrite dataconverter system
// Spigot start
if (stopBelowZero) {
chunkTag.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN).toString());
@@ -33901,7 +34132,7 @@ index ebb94d87e3b659e9049cbe5a3d5375274773fe57..4257d11f9d046249fd78fc8f453bb7c5
return this.upgradeChunkTag(chunkTag, defaultVersion, null, SharedConstants.getCurrentVersion().dataVersion().version());
}
diff --git a/net/minecraft/world/level/levelgen/structure/StructureCheck.java b/net/minecraft/world/level/levelgen/structure/StructureCheck.java
index 71799a74e2e85724de3b590728045e258c2a5df3..f47d0f31e9a16c10aa12eef7fc36841ef729816e 100644
index 71799a74e2e85724de3b590728045e258c2a5df3..6ff3a339da8ae6849985836c1138434e9b42bced 100644
--- a/net/minecraft/world/level/levelgen/structure/StructureCheck.java
+++ b/net/minecraft/world/level/levelgen/structure/StructureCheck.java
@@ -174,7 +174,7 @@ public class StructureCheck {
@@ -33909,12 +34140,12 @@ index 71799a74e2e85724de3b590728045e258c2a5df3..f47d0f31e9a16c10aa12eef7fc36841e
CompoundTag fixedChunkTag;
try {
- fixedChunkTag = DataFixTypes.CHUNK.updateToCurrentVersion(this.fixerUpper, chunkTag, version);
+ fixedChunkTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, chunkTag, version, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); // Paper - replace chunk converter
+ fixedChunkTag = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, DataFixTypes.CHUNK, chunkTag, version, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion()); // Paper - replace chunk converter
} catch (Exception e) {
LOGGER.warn("Failed to partially datafix chunk {}", pos, e);
return StructureCheckResult.CHUNK_LOAD_NEEDED;
diff --git a/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java b/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java
index e61076a9ff3d9bce5c44e548158716db105db3ee..fe1500ebbfb09bef18989c891a19608ec9d4661c 100644
index e61076a9ff3d9bce5c44e548158716db105db3ee..67b42da7175d2f81a356e59566a53265b1e70ad5 100644
--- a/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java
+++ b/net/minecraft/world/level/levelgen/structure/templatesystem/loader/TemplateSource.java
@@ -71,7 +71,7 @@ public abstract class TemplateSource {
@@ -33922,12 +34153,12 @@ index e61076a9ff3d9bce5c44e548158716db105db3ee..fe1500ebbfb09bef18989c891a19608e
StructureTemplate structureTemplate = new StructureTemplate();
int version = NbtUtils.getDataVersion(tag, 500);
- structureTemplate.load(this.blockLookup, DataFixTypes.STRUCTURE.updateToCurrentVersion(this.fixerUpper, tag, version));
+ structureTemplate.load(this.blockLookup, ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.STRUCTURE, tag, version, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion())); // Paper - rewrite dataconverter system
+ structureTemplate.load(this.blockLookup, ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.STRUCTURE, DataFixTypes.STRUCTURE, tag, version, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion())); // Paper - rewrite dataconverter system
return structureTemplate;
}
}
diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java
index 8eda63e1f07f7998875713447c5f39eea3f85ca4..eddb6ebe3fa50a818e54079b73ba117be0abd195 100644
index 8eda63e1f07f7998875713447c5f39eea3f85ca4..142a34eb046c87f16b73023c83218d8752922fbe 100644
--- a/net/minecraft/world/level/storage/PlayerDataStorage.java
+++ b/net/minecraft/world/level/storage/PlayerDataStorage.java
@@ -98,7 +98,7 @@ public class PlayerDataStorage {
@@ -33935,7 +34166,7 @@ index 8eda63e1f07f7998875713447c5f39eea3f85ca4..eddb6ebe3fa50a818e54079b73ba117b
return optTag.or(() -> this.load(nameAndId, ".dat_old")).map(tag -> {
int version = NbtUtils.getDataVersion(tag);
- return DataFixTypes.PLAYER.updateToCurrentVersion(this.fixerUpper, tag, version);
+ return ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER, tag, version, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion());
+ return ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.PLAYER, DataFixTypes.PLAYER, tag, version, ca.spottedleaf.dataconverter.minecraft.util.Version.getCurrentVersion());
});
}
File diff suppressed because it is too large. Load diff
@@ -355,7 +355,7 @@ index 0000000000000000000000000000000000000000..829f7685904fd741e00403647ae76d32
+ }
+}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index a20c7ed74a54c08b43fdf778fbf6eab4293ea0c6..7fa69d76bf2c802b538a21af40898b8abb79629e 100644
index 5090c9780ada3c8a7fd070bc6aec3ff1af034d73..a4e6225add32df93b0d6009d98ee322941b69938 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -887,6 +887,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -463,7 +463,7 @@ index f74837dc0be560a1601132594587bcfc22dcf778..c5a212e73c2efeb1f440778cb3f1fa6e
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index c9e0b378db4ca7efa018d5e9150f1c1372373f29..9601774a31012da7ec852a39a058b41b064a282a 100644
index bb61d4e4ecfe5bab6269fa3d4303dab95719d7bb..3d219969a55896b1495c54c0a12a25b49ba093f0 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -388,6 +388,15 @@ public abstract class Entity
@@ -522,7 +522,7 @@ index c9e0b378db4ca7efa018d5e9150f1c1372373f29..9601774a31012da7ec852a39a058b41b
delta = this.maybeBackOffFromEdge(delta, moverType);
Vec3 movement = this.collide(delta);
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index afbf91722c5f0d0d3d1d075d544feaf0aa187fe2..f332edbc4dad82cbdc02a58aa9ec21faf1f2db89 100644
index 62ccee5c76303b679d591714e64ee93c1502f378..0ef6c4cefb457c87f4565701817d2f9034dfa0d8 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -3380,6 +3380,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -541,19 +541,22 @@ index afbf91722c5f0d0d3d1d075d544feaf0aa187fe2..f332edbc4dad82cbdc02a58aa9ec21fa
public void tick() {
super.tick();
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index 1dcdb4788d23df187e6ae7c17629507739ae0d95..648826138efdc48487c78133893394c9e4908d3f 100644
index 554827d60aebf2a538cda9d6c96ed17aea2974b3..92ce15b22517bda5033e6377c76abaa6a9d68d03 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -216,6 +216,20 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
@@ -217,6 +217,23 @@ public abstract class Mob extends LivingEntity implements Targeting, EquipmentUs
return this.lookControl;
}
+ // Paper start
+ private int inactiveTickDisableCounter;
+
+ @Override
+ public void inactiveTick() {
+ super.inactiveTick();
+ if (!this.aware) return; // Paper - Do not tick AI for inactive unaware mobs
+ if (this.goalSelector.inactiveTick()) {
+ final boolean isThrottled = this.level().paperConfig().entities.behavior.inactiveMobGoalSelectorThrottleRate.enabled() && inactiveTickDisableCounter++ % this.level().paperConfig().entities.behavior.inactiveMobGoalSelectorThrottleRate.intValue() != 0; // Paper - Throttle inactive goal selector ticking
+ if (this.goalSelector.inactiveTick() && !isThrottled) {
+ this.goalSelector.tick();
+ }
+ if (this.targetSelector.inactiveTick()) {
@@ -641,7 +644,7 @@ index d46e0e3a4a7fc3bb3f1dd695d4cc563778b99db0..c6e9b155c2341c13d569f28f54f0c1d2
}
}
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
index b944666fa615852f6e35caf1d0a57c709a29f396..8b10b2121b61892eb4f98533c7df1ba549cdc64f 100644
index a8430d0b90acf205212b1e043e01cda8bd86e4de..a1fa75b4a02187686f1bd18f409abd2e6cbe31be 100644
--- a/net/minecraft/world/entity/item/ItemEntity.java
+++ b/net/minecraft/world/entity/item/ItemEntity.java
@@ -118,6 +118,29 @@ public class ItemEntity extends Entity implements TraceableEntity {
@@ -28,7 +28,7 @@ and then catch exceptions and close if they fire.
Part of this commit was authored by: Spottedleaf, sandtechnology
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 79968ff895c4be4ad8813a8efaed978dc2890c2a..276a4387e30c3d73a8d241f82d6d2597813ffb4e 100644
index f7814801138d9547e693ad51ba83cb2cb4ef750e..e6a674eb859af1e98fe25b644c1c5f70728128d0 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -64,7 +64,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -51,7 +51,7 @@ index 79968ff895c4be4ad8813a8efaed978dc2890c2a..276a4387e30c3d73a8d241f82d6d2597
public Connection(final PacketFlow receiving) {
this.receiving = receiving;
@@ -391,11 +395,38 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -404,11 +408,38 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
public void send(final Packet<?> packet, final @Nullable ChannelFutureListener listener, final boolean flush) {
@@ -93,7 +93,7 @@ index 79968ff895c4be4ad8813a8efaed978dc2890c2a..276a4387e30c3d73a8d241f82d6d2597
}
}
@@ -404,7 +435,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -417,7 +448,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
this.flushQueue();
action.accept(this);
} else {
@@ -102,7 +102,7 @@ index 79968ff895c4be4ad8813a8efaed978dc2890c2a..276a4387e30c3d73a8d241f82d6d2597
}
}
@@ -426,7 +457,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -439,7 +470,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
if (connected && this.pendingActions.isEmpty()) {
this.channel.eventLoop().execute(task);
} else {
@@ -111,7 +111,7 @@ index 79968ff895c4be4ad8813a8efaed978dc2890c2a..276a4387e30c3d73a8d241f82d6d2597
this.flushQueue();
}
}
@@ -459,21 +490,42 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -472,21 +503,42 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
private void doSendPacket(final Packet<?> packet, final @Nullable ChannelFutureListener listener, final boolean flush) {
@@ -158,7 +158,7 @@ index 79968ff895c4be4ad8813a8efaed978dc2890c2a..276a4387e30c3d73a8d241f82d6d2597
}
}
@@ -485,16 +537,57 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -498,16 +550,57 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
}
@@ -221,7 +221,7 @@ index 79968ff895c4be4ad8813a8efaed978dc2890c2a..276a4387e30c3d73a8d241f82d6d2597
private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world
private static int joinAttemptsThisTick; // Paper - Buffer joins to world
@@ -564,6 +657,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -577,6 +670,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void disconnect(final DisconnectionDetails details) {
this.preparing = false; // Spigot
@@ -229,7 +229,7 @@ index 79968ff895c4be4ad8813a8efaed978dc2890c2a..276a4387e30c3d73a8d241f82d6d2597
if (this.channel == null) {
this.delayedDisconnect = details;
}
@@ -753,7 +847,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -766,7 +860,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void handleDisconnection() {
if (this.channel != null && !this.channel.isOpen()) {
if (this.disconnectionHandled) {
@@ -238,7 +238,7 @@ index 79968ff895c4be4ad8813a8efaed978dc2890c2a..276a4387e30c3d73a8d241f82d6d2597
} else {
this.disconnectionHandled = true;
PacketListener packetListener = this.getPacketListener();
@@ -764,7 +858,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -777,7 +871,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
);
disconnectListener.onDisconnect(details);
}
@@ -247,7 +247,7 @@ index 79968ff895c4be4ad8813a8efaed978dc2890c2a..276a4387e30c3d73a8d241f82d6d2597
// Paper start - Add PlayerConnectionCloseEvent
if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) {
/* Player was logged in, either game listener or configuration listener */
@@ -799,4 +893,96 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -812,4 +906,96 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void setBandwidthLogger(final LocalSampleLogger bandwidthLogger) {
this.bandwidthDebugMonitor = new BandwidthDebugMonitor(bandwidthLogger);
}
@@ -381,7 +381,7 @@ index 1480d0db90f5797e3dee19503e52d1e783493ac3..bd76e55ea56dd7c1e232806d1d13a747
+ // Paper end
}
diff --git a/net/minecraft/server/network/ServerConnectionListener.java b/net/minecraft/server/network/ServerConnectionListener.java
index cb1c3b82eab2f45e550f104b0b3911ce8f852c93..c2b09a693daf8b4d3d274c07b3be37ba4fc1a483 100644
index e010f235e42d984159627f29fcd5b6885553a3b6..100e7a2483f63497a1b757c77377262de7248996 100644
--- a/net/minecraft/server/network/ServerConnectionListener.java
+++ b/net/minecraft/server/network/ServerConnectionListener.java
@@ -43,6 +43,7 @@ import org.jspecify.annotations.Nullable;
@@ -392,7 +392,7 @@ index cb1c3b82eab2f45e550f104b0b3911ce8f852c93..c2b09a693daf8b4d3d274c07b3be37ba
private static final Logger LOGGER = LogUtils.getLogger();
private final MinecraftServer server;
public volatile boolean running;
@@ -83,6 +84,7 @@ public class ServerConnectionListener {
@@ -84,6 +85,7 @@ public class ServerConnectionListener {
} catch (ChannelException var5) {
}
@@ -400,7 +400,7 @@ index cb1c3b82eab2f45e550f104b0b3911ce8f852c93..c2b09a693daf8b4d3d274c07b3be37ba
ChannelPipeline pipeline = channel.pipeline().addLast(HandlerNames.TIMEOUT, new ReadTimeoutHandler(30));
if (ServerConnectionListener.this.server.repliesToStatus()) {
pipeline.addLast(HandlerNames.LEGACY_QUERY, new LegacyQueryHandler(ServerConnectionListener.this.getServer()));
@@ -315,6 +317,7 @@ public class ServerConnectionListener {
@@ -316,6 +318,7 @@ public class ServerConnectionListener {
Connection connection;
while ((connection = this.pendingConnections.poll()) != null) {
this.connections.add(connection);
@@ -267,10 +267,10 @@ index 595db570b0ade9cad7de77d2ab7b10d9e68699df..ac095f59d9f17d8239d6493d1c1e3942
+ // Paper end - Use Velocity cipher
}
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 276a4387e30c3d73a8d241f82d6d2597813ffb4e..3b22660c9120a5de922242d717121ef53c2b0544 100644
index e6a674eb859af1e98fe25b644c1c5f70728128d0..936200d7620e1fe83eb45ddbcff86b6b6ecaab8f 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -783,10 +783,24 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -796,10 +796,24 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
return connection;
}
@@ -298,7 +298,7 @@ index 276a4387e30c3d73a8d241f82d6d2597813ffb4e..3b22660c9120a5de922242d717121ef5
public boolean isConnected() {
return this.channel != null && this.channel.isOpen();
@@ -820,16 +834,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -833,16 +847,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void setupCompression(final int threshold, final boolean validateDecompressed) {
if (threshold >= 0) {
@@ -83,7 +83,7 @@ index 255e01251005a547088aba437ca27ce06fc996e5..487fcb8546ad24125c6573fb84f22a02
ProfilerFiller profiler = Profiler.get();
this.server.spark.executeMainThreadTasks(); // Paper - spark
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index d9daa3ea8c4dc21bbe0d0ea9f0ece3f6c6777ba4..279614d017877ea2ba7c216b9cc874065f5e384d 100644
index e4d38cfdac2b926afae3b1b96679b6ce45ea256a..ca793f3f7300148cd55d2e873af5b43d9f4c708f 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1470,6 +1470,15 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -103,7 +103,7 @@ index d9daa3ea8c4dc21bbe0d0ea9f0ece3f6c6777ba4..279614d017877ea2ba7c216b9cc87406
public void save(final @Nullable ProgressListener progressListener, final boolean flush, final boolean noSave) {
// Paper start - add close param
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 194a91a2e2d9aa25f62c8a6058bc6d13a42243f5..cbd379d29d2b90b2b6fca8cd42301d12598760a8 100644
index beb0135d0c292e5b879fac5d55e5889b761d5d0a..60db677d06207b488aad0363af55012b2d76fe8b 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -212,6 +212,7 @@ import org.slf4j.Logger;
@@ -115,7 +115,7 @@ index 194a91a2e2d9aa25f62c8a6058bc6d13a42243f5..cbd379d29d2b90b2b6fca8cd42301d12
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
private static final int FLY_STAT_RECORDING_SPEED = 25;
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index b5c7affd9ac2f071992e5621a1cf3dfd335930b1..f4e2261a0ffb1d763c1e6a7b353b181178434471 100644
index 8957b3957d67ac2b1d2cfadc86ce729a485a68e2..e36089e15cdec4f0447050e87532e650bb8f3d0b 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -417,6 +417,7 @@ public abstract class PlayerList {
@@ -126,7 +126,7 @@ index b5c7affd9ac2f071992e5621a1cf3dfd335930b1..f4e2261a0ffb1d763c1e6a7b353b1811
this.playerIo.save(player);
ServerStatsCounter stats = player.getStats(); // CraftBukkit
if (stats != null) {
@@ -954,10 +955,24 @@ public abstract class PlayerList {
@@ -955,10 +956,24 @@ public abstract class PlayerList {
}
public void saveAll() {
@@ -66,10 +66,10 @@ index 487fcb8546ad24125c6573fb84f22a02752b35f1..b675e9322234761a69e71e9e8b9d4136
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 9601774a31012da7ec852a39a058b41b064a282a..cf3619b6de23f32e9e5a81fbe287fb438ecfcabc 100644
index 2cec345fdabc8e01e378e66c35093dec1fcb7c2c..0ae23c90594d85e94d17d8114a21fc62b79219d3 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -5383,6 +5383,11 @@ public abstract class Entity
@@ -5379,6 +5379,11 @@ public abstract class Entity
this.getBukkitEntity().taskScheduler.retire();
}
// Paper end - Folia schedulers
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 20 Apr 2026 02:56:22 -0700
Subject: [PATCH] Delay open/close callbacks for chests
Subject: [PATCH] Delay close callbacks for chests
The logic for chests may invoke block updates, which may not
be safe to perform. For example, inventory closing due to
@@ -9,110 +9,102 @@ player logout (which restricts chunk loading due to being
inside an entity status callback) or during shutdown (which has
chunk loading restrictions due to the chunk system being halted).
Container state changes are observable from the advancement callback that
follows a block use, so opening must remain synchronous for datapack GUIs.
diff --git a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
index a47b79c188f459e8b8f8cf0872a610b461ebde59..7053f5e3ab238d81848298f1874972ef9dc58352 100644
index a47b79c188f459e8b8f8cf0872a610b461ebde59..5ddfc7c9df08e9abedcb0fbaa0f614a804ee4059 100644
--- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
@@ -60,6 +60,13 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
private static final Component DEFAULT_NAME = Component.translatable("container.barrel");
private NonNullList<ItemStack> items = NonNullList.withSize(27, ItemStack.EMPTY);
public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() {
+ // Paper start - delay open/close callbacks
+ // Paper start - delay close callbacks
+ @Override
+ public boolean delayCallbacks() {
+ return true;
+ }
+ // Paper end - delay open/close callbacks
+ // Paper end - delay close callbacks
+
@Override
protected void onOpen(final Level level, final BlockPos pos, final BlockState state) {
BarrelBlockEntity.this.playSound(state, SoundEvents.BARREL_OPEN);
diff --git a/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/net/minecraft/world/level/block/entity/ChestBlockEntity.java
index 06878204e0df42fc35cfa380413986a045ee229d..a9273aebb5c8efe78e88158f11e1ee2af0dff3f1 100644
index 06878204e0df42fc35cfa380413986a045ee229d..c1cdced85052cbc600cf8f9d81e38ab37817a14a 100644
--- a/net/minecraft/world/level/block/entity/ChestBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/ChestBlockEntity.java
@@ -30,6 +30,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
public static final Component DEFAULT_NAME = Component.translatable("container.chest");
private NonNullList<ItemStack> items = NonNullList.withSize(27, ItemStack.EMPTY);
public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() {
+ // Paper start - delay open/close callbacks
+ // Paper start - delay close callbacks
+ @Override
+ public boolean delayCallbacks() {
+ return true;
+ }
+ // Paper end - delay open/close callbacks
+ // Paper end - delay close callbacks
+
@Override
protected void onOpen(final Level level, final BlockPos pos, final BlockState blockState) {
if (blockState.getBlock() instanceof ChestBlock chestBlock) {
diff --git a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
index baa8b5aba0500981a191626553d66650c7304b88..52737172ccfa0c7c977e4f2fe1db242e0bb92d25 100644
index baa8b5aba0500981a191626553d66650c7304b88..6aed471bbf59330d58677bb0334e14ca23dadeff 100644
--- a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
+++ b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
@@ -37,11 +37,23 @@ public abstract class ContainerOpenersCounter {
@@ -37,6 +37,12 @@ public abstract class ContainerOpenersCounter {
protected abstract void openerCountChanged(final Level level, final BlockPos pos, final BlockState blockState, int previous, int current);
+ // Paper start - delay open/close callbacks
+ // Paper start - delay close callbacks
+ public boolean delayCallbacks() {
+ return false;
+ }
+ // Paper end - delay open/close callbacks
+ // Paper end - delay close callbacks
+
public abstract boolean isOwnContainer(final Player player);
public void incrementOpeners(
final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState, final double maxInteractionRange
) {
+ // Paper start - delay open/close callbacks
+ if (this.delayCallbacks()) {
+ scheduleRecheck(level, pos, blockState, 1);
+ return;
+ }
+ // Paper end - delay open/close callbacks
int previous = this.openCount++;
// Paper start - Call BlockRedstoneEvent
@@ -66,6 +78,12 @@ public abstract class ContainerOpenersCounter {
@@ -66,6 +72,12 @@ public abstract class ContainerOpenersCounter {
}
public void decrementOpeners(final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState) {
+ // Paper start - delay open/close callbacks
+ // Paper start - delay close callbacks
+ if (this.delayCallbacks()) {
+ scheduleRecheck(level, pos, blockState, 1);
+ return;
+ }
+ // Paper end - delay open/close callbacks
+ // Paper end - delay close callbacks
if (this.openCount == 0) return; // Paper - Prevent ContainerOpenersCounter openCount from going negative
int previous = this.openCount--;
@@ -151,6 +169,11 @@ public abstract class ContainerOpenersCounter {
@@ -151,6 +163,11 @@ public abstract class ContainerOpenersCounter {
}
private static void scheduleRecheck(final Level level, final BlockPos blockPos, final BlockState blockState) {
- level.scheduleTick(blockPos, blockState.getBlock(), 5);
+ // Paper start - delay open/close callbacks
+ // Paper start - delay close callbacks
+ scheduleRecheck(level, blockPos, blockState, 5);
+ }
+ private static void scheduleRecheck(final Level level, final BlockPos blockPos, final BlockState blockState, final int delay) {
+ level.scheduleTick(blockPos, blockState.getBlock(), delay);
+ // Paper end - delay open/close callbacks
+ // Paper end - delay close callbacks
}
}
diff --git a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
index a469bef4b2e9c7d57391a5700277066cc5872ae9..6109d8da984b398bb1eb6cd6ab9a55f0511a615c 100644
index a469bef4b2e9c7d57391a5700277066cc5872ae9..c669d0ad531fc43bb7799bd011ed73ccf7b6244a 100644
--- a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
@@ -14,6 +14,13 @@ import net.minecraft.world.level.block.state.BlockState;
public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity {
private final ChestLidController chestLidController = new ChestLidController();
public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() {
+ // Paper start - delay open/close callbacks
+ // Paper start - delay close callbacks
+ @Override
+ public boolean delayCallbacks() {
+ return true;
+ }
+ // Paper end - delay open/close callbacks
+ // Paper end - delay close callbacks
+
@Override
protected void onOpen(final Level level, final BlockPos pos, final BlockState blockState) {
@@ -31,7 +31,7 @@ this fix, as the data will remain in the oversized file. Once the server returns
to a jar with this fix, the data will be restored.
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index 7767e5420e3d2e569008747523d70147ea8d97fb..b6c6bf9b20fee63b7e2a37d949bb1e55cef40de6 100644
index 7767e5420e3d2e569008747523d70147ea8d97fb..eddf7c3cef3b3209b47de97d93862937e9d2c82b 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -68,6 +68,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
@@ -111,15 +111,15 @@ index 7767e5420e3d2e569008747523d70147ea8d97fb..b6c6bf9b20fee63b7e2a37d949bb1e55
+
+ synchronized net.minecraft.nbt.CompoundTag getOversizedData(int x, int z) throws IOException {
+ Path file = getOversizedFile(x, z);
+ try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new java.util.zip.InflaterInputStream(Files.newInputStream(file))))) {
+ return net.minecraft.nbt.NbtIo.read((java.io.DataInput) out);
+ try (DataInputStream out = new DataInputStream(new java.util.zip.InflaterInputStream(Files.newInputStream(file)))) { // Paper - Optimize NBT reading/writing; FastNBTParser buffers internally
+ return ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead((java.io.DataInput) out); // Paper - Optimize NBT reading/writing
+ }
+
+ }
+ // Paper end
}
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 3b5833c5aa1dfe6045cd9ea731509227907fc6d8..c4f27907774f694c6bf2129ba14e5155c838a19b 100644
index a78e27fa5733bdb21e06e89025fd23551b3912e7..939d9a660ca9c878d2c087bad07f64dab3c06fef 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -19,6 +19,7 @@ import net.minecraft.world.level.ChunkPos;
@@ -143,7 +143,7 @@ index 3b5833c5aa1dfe6045cd9ea731509227907fc6d8..c4f27907774f694c6bf2129ba14e5155
+ synchronized (regionfile) {
+ try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) {
+ CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x(), chunkCoordinate.z());
+ CompoundTag chunk = NbtIo.read(datainputstream);
+ CompoundTag chunk = ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(datainputstream); // Paper - Optimize NBT reading/writing
+ if (oversizedData == null) {
+ return chunk;
+ }
@@ -184,7 +184,7 @@ index 3b5833c5aa1dfe6045cd9ea731509227907fc6d8..c4f27907774f694c6bf2129ba14e5155
+ // Paper end
try (DataInputStream regionChunkInputStream = region.getChunkDataInputStream(pos)) {
return regionChunkInputStream == null ? null : NbtIo.read(regionChunkInputStream);
return regionChunkInputStream == null ? null : ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(regionChunkInputStream); // Paper - Optimize NBT reading/writing
@@ -265,6 +309,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
} else {
try (DataOutputStream output = region.getChunkDataOutputStream(pos)) {
@@ -5,10 +5,10 @@ Subject: [PATCH] Remove debug-like logging
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index b84690962b9f797f95778a7ecbe0c4e398643643..8305d15e8510c09e4a3cdaa78ff1f96a620fc940 100644
index 951f5ab5a05dcedf323845f57fd061645466e563..19c7dffe5f9c45cd99cb9b14cae5ac908cae0afc 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1760,7 +1760,7 @@ public class ServerGamePacketListenerImpl
@@ -1761,7 +1761,7 @@ public class ServerGamePacketListenerImpl
private void forceSendPlayerSupportBlocks() {
if (this.tickCount >= this.lastSupportedBlocksSend + 200) {
this.lastSupportedBlocksSend = this.tickCount;
@@ -5,7 +5,7 @@ Subject: [PATCH] Anti-Xray
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
index 82377ee76caac220f7565cb729b9e608206a685a..504fdb2c4000eafae4341ec8b24ce9cb0fcdc274 100644
index 7a04214fcb23ca8cbf9ef7d50cfb28584ac4dc5f..fa322df502d1e6c6d603730372c572f62e70306a 100644
--- a/io/papermc/paper/FeatureHooks.java
+++ b/io/papermc/paper/FeatureHooks.java
@@ -47,20 +47,25 @@ public final class FeatureHooks {
@@ -174,7 +174,7 @@ index 8768e42dd359a7d7c2b7fb715482d74b3a2ee546..1b83d541378c98e3517904973e3ea4e3
@Override
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index ca793f3f7300148cd55d2e873af5b43d9f4c708f..8389d812b2d80a122fd1945b3182c08cdeda7d1c 100644
index 85ab77c3d1d5420df648fd173eaf5ad4f4775a80..ea4cc487002a0edc54cc49bfbecf72be156b6af7 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -636,7 +636,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
@@ -187,7 +187,7 @@ index ca793f3f7300148cd55d2e873af5b43d9f4c708f..8389d812b2d80a122fd1945b3182c08c
this.weatherData.setLevel(this);
this.typeKey = typeKey;
diff --git a/net/minecraft/server/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java
index 0ce4e8be3862b90ade6ddea710e47b7eb96c8942..a901aa6547c52643d11249c201688341e2961f01 100644
index 26707052187fceba34dd6661c5c397fcfcf0fc5c..81487e9840845de76f58e83cc57b173b5945bab4 100644
--- a/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -326,6 +326,7 @@ public class ServerPlayerGameMode {
@@ -215,7 +215,7 @@ index 22b10500ef74694243a4fec40822bb54ef96cc0c..196c01fe5f200ed57e6f23e0939cac61
if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), connection.getPlayer().getBukkitEntity()).callEvent();
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 459dd3ae3c9a1c303b8c4f6606d4d2f81c2a532c..a7f589970a04e520eb5cbe6dfeb333bc9cd479a8 100644
index e36089e15cdec4f0447050e87532e650bb8f3d0b..62f57eb254321902692550d0a86c24292ef9e87d 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -334,7 +334,7 @@ public abstract class PlayerList {
@@ -228,7 +228,7 @@ index 459dd3ae3c9a1c303b8c4f6606d4d2f81c2a532c..a7f589970a04e520eb5cbe6dfeb333bc
}
// Paper end - Send empty chunk
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index a1da101177a5a4fb2b4ba5e34be165daf4d147fb..21878bd770f6c0bc10dd0a67be5bd94d7e16f741 100644
index 7cbf8592ec5792e458fa07987cf258c51f9e1e8a..c14996f1d8a9ed77a845a464c1789bab0715741b 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -144,6 +144,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -266,10 +266,10 @@ index a1da101177a5a4fb2b4ba5e34be165daf4d147fb..21878bd770f6c0bc10dd0a67be5bd94d
// CraftBukkit start - remove blockstate if failed (or the same)
if (this.captureBlockStates && captured) {
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
index 807529f25ed0ef03beef6746f706d8a215e2b54c..d0d435f53683bae1a4c333aed18cc36c9797a188 100644
index 9e66f6202439cc7780af1016289f3b08dc3e0766..39ea7198f3bbe4525e686504887e0074de80854d 100644
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -159,7 +159,7 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeR
@@ -175,7 +175,7 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeR
}
}
@@ -278,7 +278,7 @@ index 807529f25ed0ef03beef6746f706d8a215e2b54c..d0d435f53683bae1a4c333aed18cc36c
// Paper start - rewrite chunk system
if (!((Object)this instanceof ImposterProtoChunk)) {
this.starlight$setBlockNibbles(ca.spottedleaf.moonrise.patches.starlight.light.StarLightEngine.getFilledEmptyLight(levelHeightAccessor));
@@ -172,10 +172,10 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeR
@@ -188,10 +188,10 @@ public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeR
// Paper end - get block chunk optimisation
}
@@ -292,10 +292,10 @@ index 807529f25ed0ef03beef6746f706d8a215e2b54c..d0d435f53683bae1a4c333aed18cc36c
}
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 0e5869aaeac4063fbd3e985feda290cc43a578e6..af4106497a69f9b311970197016c6157475a7d85 100644
index e482afb37a061ab720b72c4604dfafa42a198cd9..76dae652c910e36c860791a8285bd9fee466d5b9 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -150,7 +150,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -155,7 +155,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
final LevelChunk.@Nullable PostLoadProcessor postLoad,
final @Nullable BlendingData blendingData
) {
@@ -305,7 +305,7 @@ index 0e5869aaeac4063fbd3e985feda290cc43a578e6..af4106497a69f9b311970197016c6157
this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>();
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
index b2c6bcb8d82fc01bf4e8540c6d9f6bc94feb3659..dc0270de6d13b7b6fc5de463fab05e9cca842480 100644
index 54ccea30b7bd79304cbe00434cfd4ba5586d7493..4e1359f85d66185c57a27302fd4672cd7e265874 100644
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -56,8 +56,14 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
@@ -324,7 +324,7 @@ index b2c6bcb8d82fc01bf4e8540c6d9f6bc94feb3659..dc0270de6d13b7b6fc5de463fab05e9c
this.biomes = containerFactory.createForBiomes();
}
@@ -279,11 +285,17 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
@@ -277,11 +283,17 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
this.biomes = biomes;
}
@@ -344,14 +344,23 @@ index b2c6bcb8d82fc01bf4e8540c6d9f6bc94feb3659..dc0270de6d13b7b6fc5de463fab05e9c
}
public int getSerializedSize() {
@@ -309,7 +321,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
// in a single-value palette instead of retaining an unused default biome.
// Sampling order must match Vanilla, as the RTree caches the last result (MC-241546).
final Holder<Biome> first = biomeResolver.getNoiseBiome(quartMinX, quartMinY, quartMinZ);
- final PalettedContainer<Holder<Biome>> newBiomes = new PalettedContainer<>(first, this.biomes.strategy);
+ final PalettedContainer<Holder<Biome>> newBiomes = new PalettedContainer<>(first, this.biomes.strategy, null); // Paper - Anti-Xray
for (int x = 0; x < 4; ++x) {
for (int y = 0; y < 4; ++y) {
diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java
index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbbc0604d36 100644
index 268f73d632691656e7e1244972bf9075ad2994dc..d564338ddc3dc13f0d11a18e26ea36e1f9343745 100644
--- a/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -26,6 +26,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
private static final int MIN_PALETTE_BITS = 0;
public volatile PalettedContainer.Data<T> data; // Paper - optimise collisions - public
private final Strategy<T> strategy;
public final Strategy<T> strategy; // Paper - optimise chunk generation - public
+ private final T @Nullable [] presetValues; // Paper - Anti-Xray - Add preset values
//private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused
@@ -379,7 +388,7 @@ index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbb
return codec(elementCodec, strategy, defaultValue, unpacker);
}
@@ -91,19 +99,57 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -74,17 +82,60 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
}
// Paper end - optimise palette reads
@@ -391,7 +400,6 @@ index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbb
+ // Paper end - Anti-Xray - Add preset values
this.strategy = strategy;
this.data = new PalettedContainer.Data<>(dataConfiguration, storage, palette);
this.updateData(this.data); // Paper - optimise palette reads
+ // Paper start - Anti-Xray
+ if (presetValues != null
+ && (dataConfiguration instanceof Configuration.Simple simpleFactory && simpleFactory.factory() == Strategy.SINGLE_VALUE_PALETTE_FACTORY
@@ -401,20 +409,25 @@ index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbb
+ // We readd this here but in a smarter way than it was before
+ int maxSize = 1 << dataConfiguration.bitsInMemory();
+
+ for (T presetValue : presetValues) {
+ for (int i = 0; i < presetValues.length; i++) {
+ if (this.data.palette.getSize() >= maxSize) {
+ java.util.Set<T> allValues = new java.util.HashSet<>(values);
+ int duplicates = values.size() - allValues.size();
+ allValues.addAll(Arrays.asList(presetValues));
+ int newBits = net.minecraft.util.Mth.ceillog2(allValues.size());
+ int newBits = net.minecraft.util.Mth.ceillog2(allValues.size() + duplicates);
+
+ if (newBits > dataConfiguration.bitsInMemory()) {
+ this.onResize(newBits, null);
+ } else {
+ for (int j = i; j < presetValues.length; j++) {
+ this.data.palette.idFor(presetValues[j], this);
+ }
+ }
+
+ break;
+ }
+
+ this.data.palette.idFor(presetValue, this);
+ this.data.palette.idFor(presetValues[i], this);
+ }
+ }
+ // Paper end - Anti-Xray
@@ -425,7 +438,6 @@ index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbb
+ this.presetValues = presetValues; // Paper - Anti-Xray - Add preset values
this.strategy = source.strategy;
this.data = source.data.copy();
this.updateData(this.data); // Paper - optimise palette reads
}
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Anti-Xray
@@ -439,7 +451,7 @@ index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbb
this.strategy = strategy;
this.data = this.createOrReuseData(null, 0);
this.data.palette.idFor(initialValue, this);
@@ -123,15 +169,39 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -103,14 +154,38 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
return new PalettedContainer.Data<>(dataConfiguration, storage, palette);
}
@@ -470,7 +482,6 @@ index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbb
PalettedContainer.Data<T> newData = this.createOrReuseData(oldData, bits);
newData.copyFrom(oldData.palette, oldData.storage);
this.data = newData;
this.updateData(this.data); // Paper - optimise palette reads
- return newData.palette.idFor(lastAddedValue, PaletteResize.noResizeExpected());
+ // Paper start - Anti-Xray
+ this.addPresetValues();
@@ -481,10 +492,10 @@ index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbb
public synchronized T getAndSet(final int x, final int y, final int z, final T value) { // Paper - synchronize
this.acquire();
@@ -201,24 +271,38 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -179,24 +254,38 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
newData.palette.read(buffer, this.strategy.globalMap());
buffer.readFixedSizeLongArray(newData.storage.getRaw());
this.data = newData;
this.updateData(this.data); // Paper - optimise palette reads
+ this.addPresetValues(); // Paper - Anti-Xray - Add preset values (inefficient, but this isn't used by the server)
} finally {
this.release();
@@ -522,7 +533,7 @@ index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbb
List<T> paletteEntries = discData.paletteEntries();
int entryCount = strategy.entryCount();
Configuration storedConfiguration = strategy.getConfigurationForPaletteSize(paletteEntries.size());
@@ -257,7 +341,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -235,7 +324,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
}
}
@@ -531,7 +542,7 @@ index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbb
}
// Paper start - optimise PalettedContainer packing
@@ -404,12 +488,12 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -382,12 +471,12 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@Override
public PalettedContainer<T> copy() {
@@ -546,7 +557,7 @@ index 9213568da8d590b2f81ba3a51e216ec395badb31..55037971b2631100ac5fd5fd8157adbb
}
@Override
@@ -491,9 +575,16 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -433,9 +522,16 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
return 1 + this.palette.getSerializedSize(globalMap) + this.storage.getRaw().length * 8;
}
@@ -621,7 +632,7 @@ index 8ce60c968bf8c9be2d6f21cc16762627cbc23219..45e95faebdb81485dc9b3823601f4506
int getSerializedSize();
diff --git a/net/minecraft/world/level/chunk/Strategy.java b/net/minecraft/world/level/chunk/Strategy.java
index 5c518b6ec7da546460884c37e9af682b6ab9fc22..958be147cd940ac430576ca5b4ada70fa5d051e3 100644
index 8fc956f434cd42fbe67316df36c125714ada9552..38e4abe3307c020ec7e2385a559ff4708b09c899 100644
--- a/net/minecraft/world/level/chunk/Strategy.java
+++ b/net/minecraft/world/level/chunk/Strategy.java
@@ -4,7 +4,7 @@ import net.minecraft.core.IdMap;
@@ -631,7 +642,7 @@ index 5c518b6ec7da546460884c37e9af682b6ab9fc22..958be147cd940ac430576ca5b4ada70f
- private static final Palette.Factory SINGLE_VALUE_PALETTE_FACTORY = SingleValuePalette::create;
+ public static final Palette.Factory SINGLE_VALUE_PALETTE_FACTORY = SingleValuePalette::create; // Paper - Anti-Xray
private static final Palette.Factory LINEAR_PALETTE_FACTORY = LinearPalette::create;
private static final Palette.Factory HASHMAP_PALETTE_FACTORY = HashMapPalette::create;
private static final Palette.Factory HASHMAP_PALETTE_FACTORY = ca.spottedleaf.moonrise.patches.fast_palette.FastHashMapPalette::create; // Paper - optimise palette reads
private static final Configuration ZERO_BITS = new Configuration.Simple(SINGLE_VALUE_PALETTE_FACTORY, 0);
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index 5b311dc7f0a9d7cb202011302920478977f27bb0..84a92dc868e61969460dddb156af332e6384bcd1 100644
@@ -55,7 +55,7 @@ index a6f9435a8e2a6c8cc05bd3873cb6fc0ec98973b8..d44fd9eea0ccb09c894d6431ede8a4c2
this.used.set(position, position + size);
}
diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java
index 3ba442ac6b823e1c6a69398abc4f116da07c32c0..3de7fd2b084c38e72d7a6bc416880a881f514ad3 100644
index d1ea7f442e258b06f5c879de7d57fd16caf09178..5e64762059846f14d4e8ddfec1d1640c199cece2 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -46,6 +46,363 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche
@@ -104,7 +104,7 @@ index 3ba442ac6b823e1c6a69398abc4f116da07c32c0..3de7fd2b084c38e72d7a6bc416880a88
+
+ InputStream input = compression.wrap(new ByteArrayInputStream(chunkData.array(), chunkData.position(), chunkDataLength - chunkData.position()));
+
+ return net.minecraft.nbt.NbtIo.read(new DataInputStream(input));
+ return ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(new DataInputStream(input)); // Paper - Optimize NBT reading/writing
+ } catch (Exception ex) {
+ return null;
+ }
@@ -264,7 +264,7 @@ index 3ba442ac6b823e1c6a69398abc4f116da07c32c0..3de7fd2b084c38e72d7a6bc416880a88
+ for (RegionFileVersion compressionType : RegionFileVersion.VERSIONS.values()) {
+ try {
+ DataInputStream in = new DataInputStream(compressionType.wrap(new ByteArrayInputStream(chunkData))); // typical java
+ compound = net.minecraft.nbt.NbtIo.read((java.io.DataInput)in);
+ compound = ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead((java.io.DataInput)in); // Paper - Optimize NBT reading/writing
+ compression = compressionType;
+ break; // reaches here iff readNBT does not throw
+ } catch (Exception ex) {
@@ -639,7 +639,7 @@ index 3ba442ac6b823e1c6a69398abc4f116da07c32c0..3de7fd2b084c38e72d7a6bc416880a88
return stub;
}
diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index a2dd62ced02de213ffe1244b893069b93011713c..154b571b33c868c6520ab563d8cd5482f1c288c3 100644
index 4d3abebdb8b5f952837acb3093ff6e04c44b08e4..6db9314f12dd69ab82d689ab77b7cc6bd53af102 100644
--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -27,6 +27,32 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
@@ -703,9 +703,9 @@ index a2dd62ced02de213ffe1244b893069b93011713c..154b571b33c868c6520ab563d8cd5482
final int chunkX, final int chunkZ, final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData readData
) throws IOException {
try {
- return NbtIo.read(readData.input());
- return ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(readData.input()); // Paper - Optimize NBT reading/writing
+ // Paper start - Attempt to recalculate regionfile header if it is corrupt
+ final CompoundTag ret = NbtIo.read(readData.input());
+ final CompoundTag ret = ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(readData.input()); // Paper - Optimize NBT reading/writing
+ if (!this.isChunkData) {
+ return ret;
+ }
@@ -745,13 +745,13 @@ index a2dd62ced02de213ffe1244b893069b93011713c..154b571b33c868c6520ab563d8cd5482
// Paper end
try (DataInputStream regionChunkInputStream = region.getChunkDataInputStream(pos)) {
- return regionChunkInputStream == null ? null : NbtIo.read(regionChunkInputStream);
- return regionChunkInputStream == null ? null : ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(regionChunkInputStream); // Paper - Optimize NBT reading/writing
+ // Paper start - recover from corrupt regionfile header
+ if (regionChunkInputStream == null) {
+ return null;
+ }
+
+ final CompoundTag serialisedChunkData = NbtIo.read(regionChunkInputStream);
+ final CompoundTag serialisedChunkData = ca.spottedleaf.moonrise.patches.nbt_rw.FastNBTParser.nbtIoRead(regionChunkInputStream); // Paper - Optimize NBT reading/writing
+ if (this.isChunkData) {
+ ChunkPos headerChunkPos = SerializableChunkData.getChunkCoordinate(serialisedChunkData);
+ if (!headerChunkPos.equals(pos)) {
@@ -832,7 +832,7 @@ index 84a92dc868e61969460dddb156af332e6384bcd1..3fef98e8c3c9908c6f05ded33596cbcb
tag.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString());
tag.storeNullable("blending_data", BlendingData.Packed.CODEC, this.blendingData);
diff --git a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
index df4f03f3a346e0ab0074757b3a077b2407135ac3..0b0265c8d4cb70aef758c1b443d007ef8d6c282b 100644
index cdbe291d2053239e3b8dd0042dfb475306a6b6e5..12ac13394fc38f089be0ad7731eb4ea9487e1d17 100644
--- a/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
+++ b/net/minecraft/world/level/chunk/storage/SimpleRegionStorage.java
@@ -34,6 +34,7 @@ public class SimpleRegionStorage implements ca.spottedleaf.moonrise.patches.chun
@@ -6,7 +6,7 @@ Subject: [PATCH] Optimise collision checking in player move packet handling
Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 8305d15e8510c09e4a3cdaa78ff1f96a620fc940..cc0d455f88bc7c5798ea27abfaa3333c2656eb92 100644
index 19c7dffe5f9c45cd99cb9b14cae5ac908cae0afc..1c92e33104cc131269be15333eeee0ca5f0c92b1 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -647,6 +647,7 @@ public class ServerGamePacketListenerImpl
@@ -71,7 +71,7 @@ index 8305d15e8510c09e4a3cdaa78ff1f96a620fc940..cc0d455f88bc7c5798ea27abfaa3333c
}
@Override
@@ -1576,7 +1611,7 @@ public class ServerGamePacketListenerImpl
@@ -1577,7 +1612,7 @@ public class ServerGamePacketListenerImpl
}
}
@@ -80,7 +80,7 @@ index 8305d15e8510c09e4a3cdaa78ff1f96a620fc940..cc0d455f88bc7c5798ea27abfaa3333c
xDist = targetX - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above
yDist = targetY - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above
zDist = targetZ - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above
@@ -1606,6 +1641,7 @@ public class ServerGamePacketListenerImpl
@@ -1607,6 +1642,7 @@ public class ServerGamePacketListenerImpl
this.player.move(MoverType.PLAYER, new Vec3(xDist, yDist, zDist));
this.player.onGround = isOnGround; // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
@@ -88,7 +88,7 @@ index 8305d15e8510c09e4a3cdaa78ff1f96a620fc940..cc0d455f88bc7c5798ea27abfaa3333c
// Paper start - prevent position desync
if (this.awaitingPositionFromClient != null) {
return; // ... thanks Mojang for letting move calls teleport across dimensions.
@@ -1639,7 +1675,17 @@ public class ServerGamePacketListenerImpl
@@ -1640,7 +1676,17 @@ public class ServerGamePacketListenerImpl
}
// Paper start - Add fail move event
@@ -107,7 +107,7 @@ index 8305d15e8510c09e4a3cdaa78ff1f96a620fc940..cc0d455f88bc7c5798ea27abfaa3333c
if (!allowMovement) {
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK,
targetX, targetY, targetZ, targetYRot, targetXRot, false);
@@ -1787,7 +1833,7 @@ public class ServerGamePacketListenerImpl
@@ -1788,7 +1834,7 @@ public class ServerGamePacketListenerImpl
private boolean updateAwaitingTeleport() {
if (this.awaitingPositionFromClient != null) {
@@ -116,7 +116,7 @@ index 8305d15e8510c09e4a3cdaa78ff1f96a620fc940..cc0d455f88bc7c5798ea27abfaa3333c
this.awaitingTeleportTime = this.tickCount;
this.teleport(
this.awaitingPositionFromClient.x,
@@ -1806,6 +1852,34 @@ public class ServerGamePacketListenerImpl
@@ -1807,6 +1853,34 @@ public class ServerGamePacketListenerImpl
}
}
@@ -5,7 +5,7 @@ Subject: [PATCH] Optional per player mob spawns
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 206a897e9d74b48d38e2ea1652e9b7ed4735f828..fee899044c389003a1f976f04b752bc78e2bfb69 100644
index 0a60283625536515bfcf6c2a09ca0f4771d6b142..42500899812fe3ef18c09f68a85012b705c1010d 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -237,11 +237,29 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP
@@ -42,10 +42,10 @@ index 206a897e9d74b48d38e2ea1652e9b7ed4735f828..fee899044c389003a1f976f04b752bc7
protected ChunkGenerator generator() {
return this.worldGenContext.generator();
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index a6fb79a70de177fcc027ddb0c5ae6fe3ed3c0407..c1f6f20247e85c98fbefaee707c1532e8bca64f1 100644
index 2dc8b94c434e841b319ef5b77bd725fd66f3c10b..a5540612a4052d9e9d8b1b0ac3c1591777f26caa 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -534,7 +534,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -547,7 +547,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
private void tickChunks(final ProfilerFiller profiler) {
profiler.push("naturalSpawnCount");
int chunkCount = this.distanceManager.getNaturalSpawnChunkCount();
@@ -65,7 +65,7 @@ index a6fb79a70de177fcc027ddb0c5ae6fe3ed3c0407..c1f6f20247e85c98fbefaee707c1532e
this.lastSpawnState = spawnCookie;
boolean doMobSpawning = this.level.getGameRules().get(GameRules.SPAWN_MOBS) && !this.level.players().isEmpty(); // CraftBukkit
int tickSpeed = this.level.getGameRules().get(GameRules.RANDOM_TICK_SPEED);
@@ -562,7 +573,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -575,7 +586,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
profiler.popPush("shuffleSpawningChunks");
// Paper start - chunk tick iteration optimisation
this.shuffleRandom.setSeed(this.level.getRandom().nextLong());
@@ -75,7 +75,7 @@ index a6fb79a70de177fcc027ddb0c5ae6fe3ed3c0407..c1f6f20247e85c98fbefaee707c1532e
profiler.popPush("tickSpawningChunks");
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index cbd379d29d2b90b2b6fca8cd42301d12598760a8..5185dd1f05c166f4ed5891130d1deb6ee551169a 100644
index 2cf8854636df72c1dccbb73dfe8f642dfdf1ea37..99403edc1eda1e238b66028b7d236e5fceb235a1 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -422,6 +422,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -6,7 +6,7 @@ Subject: [PATCH] Improve cancelling PreCreatureSpawnEvent with per player mob
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index fee899044c389003a1f976f04b752bc78e2bfb69..b4d62fac8af59f98463f55f2b864343baeefc79d 100644
index 42500899812fe3ef18c09f68a85012b705c1010d..fdcbf132e2045bc6828028556ca4c3549d9abec7 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -256,8 +256,25 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP
@@ -37,10 +37,10 @@ index fee899044c389003a1f976f04b752bc78e2bfb69..b4d62fac8af59f98463f55f2b864343b
// Paper end - Optional per player mob spawns
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index c1f6f20247e85c98fbefaee707c1532e8bca64f1..66d706d2a7995b437d102903012a714c38e4876c 100644
index a5540612a4052d9e9d8b1b0ac3c1591777f26caa..c4cfb53e145d69666ffc27ef4ee0c50b54eb9143 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -539,7 +539,17 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -552,7 +552,17 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
// re-set mob counts
for (ServerPlayer player : this.level.players()) {
@@ -60,7 +60,7 @@ index c1f6f20247e85c98fbefaee707c1532e8bca64f1..66d706d2a7995b437d102903012a714c
spawnCookie = NaturalSpawner.createState(chunkCount, this.level, this::getFullChunk, null, true);
} else {
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 5185dd1f05c166f4ed5891130d1deb6ee551169a..4227d7b652b989bb86bff6960545c323dc535e46 100644
index 99403edc1eda1e238b66028b7d236e5fceb235a1..23b3a82ab3e1c6abc85000d9285470cb12300a6b 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -426,6 +426,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -5,10 +5,10 @@ Subject: [PATCH] DataConverter, Moonrise & co fixes
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index cc0d455f88bc7c5798ea27abfaa3333c2656eb92..e24c7e2db785bce4a8801373d8e2bcd25477dbb6 100644
index 1c92e33104cc131269be15333eeee0ca5f0c92b1..6dc9e669a37ddc0a17099bcc4839b42fe237cf99 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1856,9 +1856,14 @@ public class ServerGamePacketListenerImpl
@@ -1857,9 +1857,14 @@ public class ServerGamePacketListenerImpl
private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) {
final List<AABB> collisionsBB = new java.util.ArrayList<>();
final List<VoxelShape> collisionsVoxel = new java.util.ArrayList<>();
@@ -48,10 +48,10 @@ index cff05f64af7f425b77cbbd7680c5cc592faa798c..0ab4080c8e30d5ee14ce5fcbac64b031
serverEntity.getLastSentYRot(),
entity.getType(),
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index b4d62fac8af59f98463f55f2b864343baeefc79d..3ed912fb19e1dfda7987fcf4b504aff5abccc46c 100644
index fdcbf132e2045bc6828028556ca4c3549d9abec7..fd0b9f20fbdd523d51550fd6009b5011e6e7e6e6 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -1413,6 +1413,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP
@@ -1447,6 +1447,7 @@ public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerP
ChunkMap.this.level.debugSynchronizers().startTrackingEntity(player, this.entity);
}
// Paper end - entity tracking events
@@ -104,7 +104,7 @@ index d3f17088d8f0592ef1231cf3f8bb3ca83170de0b..ddda24a55201de2136fc6dd2cb863d60
}
}
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa08ae9ec08 100644
index 25b255d0590181f47500c8ee338f2925432b8add..803e36f33c706d8484739cb21af25b9289dc8808 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -142,6 +142,43 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -166,7 +166,7 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
changed |= action.getAsBoolean();
}
@@ -179,67 +217,218 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -179,61 +217,210 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
return true;
}
@@ -181,8 +181,10 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
+
+ private static boolean hopperPush(final Level level, final Container destination, final Direction direction, final HopperBlockEntity hopper) {
+ skipPushModeEventFire = skipHopperEvents;
+ final long disallowedSlots = callPrepareTransferEvent(level, hopper, destination, true); // Paper - Add PrepareHopperTransferEvent
+ boolean foundItem = false;
+ for (int i = 0; i < hopper.getContainerSize(); ++i) {
+ if (isSlotDisallowed(disallowedSlots, i)) continue; // Paper - Add PrepareHopperTransferEvent
+ final ItemStack item = hopper.getItem(i);
+ if (!item.isEmpty()) {
+ foundItem = true;
@@ -217,10 +219,10 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
+ }
+ origItemStack.setCount(originalItemCount);
+ }
}
+ }
+ if (foundItem && level.paperConfig().hopper.cooldownWhenFull) { // Inventory was full - cooldown
+ hopper.setCooldown(level.spigotConfig.hopperTransfer);
+ }
}
+ return false;
+ }
@@ -231,7 +233,6 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
+ ItemStack movedItem = origItemStack;
+ final int originalItemCount = origItemStack.getCount();
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
+ container.setChanged(); // original logic always marks source inv as changed even if no move happens.
+ movedItem.setCount(movedItemCount);
+
+ if (!skipPullModeEventFire) {
@@ -243,27 +244,8 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
+ // site for IMIE did not exhibit the same behavior
+ return true;
+ }
}
- for (int slot = 0; slot < self.getContainerSize(); slot++) {
- ItemStack itemStack = self.getItem(slot);
- if (!itemStack.isEmpty()) {
- int originalCount = itemStack.getCount();
- // CraftBukkit start - Call event when pushing items into other inventories
- ItemStack original = itemStack.copy();
- org.bukkit.inventory.ItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitMirror(
- self.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot
- );
-
- org.bukkit.inventory.Inventory destinationInventory;
- // Have to special case large chests as they work oddly
- if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
- } else if (container.getOwner() != null) {
- destinationInventory = container.getOwner().getInventory();
- } else {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
- }
+ }
+
+ final ItemStack remainingItem = addItem(container, hopper, movedItem, null);
+ final int remainingItemCount = remainingItem.getCount();
+ if (remainingItemCount != movedItemCount) {
@@ -281,10 +263,39 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
+ }
+ origItemStack.setCount(originalItemCount);
+
+ Container updatedContainer = container;
+ if (container instanceof net.minecraft.world.CompoundContainer compoundContainer) {
+ updatedContainer = i >= compoundContainer.container1.getContainerSize() ? compoundContainer.container2 : compoundContainer.container1;
+ }
+ updatedContainer.setChanged();
+
+ if (level.paperConfig().hopper.cooldownWhenFull) {
+ applyCooldown(hopper);
+ }
+
}
- final long disallowedSlots = callPrepareTransferEvent(level, self, container, true); // Paper - Add PrepareHopperTransferEvent
- for (int slot = 0; slot < self.getContainerSize(); slot++) {
- if (isSlotDisallowed(disallowedSlots, slot)) continue; // Paper - Add PrepareHopperTransferEvent
- ItemStack itemStack = self.getItem(slot);
- if (!itemStack.isEmpty()) {
- int originalCount = itemStack.getCount();
- // CraftBukkit start - Call event when pushing items into other inventories
- ItemStack original = itemStack.copy();
- org.bukkit.inventory.ItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitMirror(
- self.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot
- );
-
- final org.bukkit.inventory.Inventory destinationInventory = getInventory(container); // Paper - Add PrepareHopperTransferEvent
-
- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
- self.getOwner().getInventory(),
- oitemstack,
- destinationInventory,
- true
- );
- if (!event.callEvent()) {
- self.setItem(slot, original);
- self.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
+ return false;
+ }
+
@@ -336,36 +347,12 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
+ }
+ }
+
+ private static org.bukkit.inventory.Inventory getInventory(final Container container) {
+ final org.bukkit.inventory.Inventory sourceInventory;
+ if (container instanceof net.minecraft.world.CompoundContainer compoundContainer) {
+ // Have to special-case large chests as they work oddly
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
+ } else if (container instanceof BlockEntity blockEntity) {
+ sourceInventory = blockEntity.getOwner(false).getInventory();
+ } else if (container.getOwner() != null) {
+ sourceInventory = container.getOwner().getInventory();
+ } else {
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
+ }
+ return sourceInventory;
+ }
+
+ private static void applyCooldown(final Hopper hopper) {
+ if (hopper instanceof HopperBlockEntity blockEntity && blockEntity.getLevel() != null) {
+ blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer);
+ }
+ }
- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
- self.getOwner().getInventory(),
- oitemstack,
- destinationInventory,
- true
- );
- if (!event.callEvent()) {
- self.setItem(slot, original);
- self.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
+
+ private static boolean allMatch(Container container, Direction direction, java.util.function.BiPredicate<ItemStack, Integer> test) {
+ if (container instanceof WorldlyContainer worldlyContainer) {
+ for (int slot : worldlyContainer.getSlotsForFace(direction)) {
@@ -401,29 +388,29 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
+ for (int slot : worldlyContainer.getSlotsForFace(direction)) {
+ if (test.test(container.getItem(slot), slot)) {
+ return true;
+ }
+ }
}
}
+ } else {
+ int size = container.getContainerSize();
+ for (int slot = 0; slot < size; slot++) {
+ if (test.test(container.getItem(slot), slot)) {
+ return true;
}
}
}
+ }
+ }
+ }
+ return true;
+ }
+ private static final java.util.function.BiPredicate<ItemStack, Integer> STACK_SIZE_TEST = (itemStack, _) -> itemStack.getCount() >= itemStack.getMaxStackSize();
+ private static final java.util.function.BiPredicate<ItemStack, Integer> IS_EMPTY_TEST = (itemStack, _) -> itemStack.isEmpty();
+ // Paper end - Perf: Optimize Hoppers
- return false;
+
+ private static boolean ejectItems(final Level level, final BlockPos blockPos, final HopperBlockEntity self) {
+ Container container = getAttachedContainer(level, blockPos, self);
+ if (container == null) {
+ return false;
+ }
+
}
- return false;
+ Direction direction = self.facing.getOpposite();
+ if (isFullContainer(container, direction)) {
+ return false;
@@ -432,16 +419,27 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
+ return hopperPush(level, container, direction, self); // Paper - Perf: Optimize Hoppers
}
private static int[] getSlots(final Container container, final Direction direction) {
@@ -292,6 +481,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
// Paper start - Add PrepareHopperTransferEvent
@@ -255,6 +442,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (container instanceof net.minecraft.world.CompoundContainer compoundContainer) {
// Have to special-case large chests as they work oddly
sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
+ // Paper start - Perf: Optimize Hoppers
+ } else if (container instanceof BlockEntity blockEntity) {
+ sourceInventory = blockEntity.getOwner(false).getInventory();
+ // Paper end - Perf: Optimize Hoppers
} else if (container.getOwner() != null) {
sourceInventory = container.getOwner().getInventory();
} else {
@@ -314,6 +505,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
Container container = getSourceContainer(level, hopper, blockPos, blockState);
if (container != null) {
Direction direction = Direction.DOWN;
+ skipPullModeEventFire = skipHopperEvents; // Paper - Perf: Optimize Hoppers
final long disallowedSlots = callPrepareTransferEvent(level, container, hopper, false); // Paper - Add PrepareHopperTransferEvent
for (int slot : getSlots(container, direction)) {
if (tryTakeInItemFromSlot(hopper, container, slot, direction, level)) { // Spigot
@@ -319,54 +509,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -343,46 +535,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
private static boolean tryTakeInItemFromSlot(final Hopper hopper, final Container container, final int slot, final Direction direction, final Level level) { // Spigot
ItemStack itemStack = container.getItem(slot);
if (!itemStack.isEmpty() && canTakeItemFromContainer(hopper, container, itemStack, slot, direction)) {
@@ -452,15 +450,7 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
- container.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot
- );
-
- org.bukkit.inventory.Inventory sourceInventory;
- // Have to special case large chests as they work oddly
- if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) {
- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
- } else if (container.getOwner() != null) {
- sourceInventory = container.getOwner().getInventory();
- } else {
- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
- }
- final org.bukkit.inventory.Inventory sourceInventory = getInventory(container); // Paper - Add PrepareHopperTransferEvent
-
- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
- sourceInventory,
@@ -497,7 +487,7 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
}
return false;
@@ -375,13 +518,15 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -391,13 +544,15 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
public static boolean addItem(final Container container, final ItemEntity entity) {
boolean changed = false;
// CraftBukkit start
@@ -514,7 +504,7 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
ItemStack copy = entity.getItem().copy();
ItemStack result = addItem(null, container, copy, null);
if (result.isEmpty()) {
@@ -440,7 +585,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -456,7 +611,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
itemStack = itemStack.split(container.getMaxStackSize());
}
// Spigot end
@@ -524,7 +514,7 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
itemStack = leftover; // Paper - Make hoppers respect inventory max stack size
success = true;
} else if (canMergeItems(current, itemStack)) {
@@ -526,14 +673,22 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -542,14 +699,22 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
public static @Nullable Container getContainerAt(final Level level, final BlockPos pos) {
@@ -549,7 +539,7 @@ index 30f681ba31c3b7997ef0d1d6153e9339fb4b2d3b..86f005e755f58f9f6a674a5065444fa0
result = getEntityContainer(level, x, y, z);
}
@@ -557,14 +712,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -573,14 +738,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
private static @Nullable Container getEntityContainer(final Level level, final double x, final double y, final double z) {
@@ -0,0 +1,140 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Doc <git@mrdoc.dev>
Date: Wed, 30 Sep 2026 23:14:44 -0300
Subject: [PATCH] Backport MC-311924 fix to 26.3
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 0ef6c4cefb457c87f4565701817d2f9034dfa0d8..23297b54c74684765f90e9e599d051a44357e9a5 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -278,7 +278,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
private long lastKineticHitFeedbackTime = -2147483648L;
private BlockPos lastPos;
private Optional<BlockPos> lastClimbablePos = Optional.empty();
- private @Nullable DamageSource lastDamageSource;
+ protected @Nullable DamageSource lastDamageSource; // Paper - Backport MC-311924 fix
private long lastDamageStamp;
protected int autoSpinAttackTicks;
protected float autoSpinAttackDmg;
diff --git a/net/minecraft/world/entity/decoration/ArmorStand.java b/net/minecraft/world/entity/decoration/ArmorStand.java
index b842fa8de45c0b25912e40baa7ede5ebc259fa81..0ffcf91694269568ae5e63cbd325e622323e220e 100644
--- a/net/minecraft/world/entity/decoration/ArmorStand.java
+++ b/net/minecraft/world/entity/decoration/ArmorStand.java
@@ -577,10 +577,11 @@ public class ArmorStand extends LivingEntity {
if (callEvent && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDeathEvent(level, this, (source == null ? this.damageSources().genericKill() : source))) {
return;
}
- final Entity attributedTo = source == null ? null : source.getEntity();
// Paper end - pass damage source for event
+ // final Entity attributedTo = source == null ? null : source.getEntity(); // Paper - Backport MC-311924 fix
+ this.lastDamageSource = source; // // Paper - Backport MC-311924 fix
this.remove(Entity.RemovalReason.KILLED);
- this.gameEvent(GameEvent.ENTITY_DIE, attributedTo != null ? attributedTo : this);
+ this.gameEvent(GameEvent.ENTITY_DIE); // Paper - Backport MC-311924 fix
}
@Override
diff --git a/net/minecraft/world/entity/decoration/BlockAttachedEntity.java b/net/minecraft/world/entity/decoration/BlockAttachedEntity.java
index 40f65ee2126a8310f9e289a9da76c8a3bd236894..0526ac2faf920185ec8079d7f5b7969b92a8b2c0 100644
--- a/net/minecraft/world/entity/decoration/BlockAttachedEntity.java
+++ b/net/minecraft/world/entity/decoration/BlockAttachedEntity.java
@@ -26,6 +26,7 @@ public abstract class BlockAttachedEntity extends Entity {
private static final int CHECK_INTERVAL = 100;
private int ticksSinceLastCheck; { this.ticksSinceLastCheck = this.getId() % this.level().spigotConfig.hangingTickFrequency; } // Paper - Perf: offset item frame ticking
protected BlockPos pos;
+ protected @Nullable DamageSource lastDamageSource; // Paper - Backport MC-311924 fix
protected BlockAttachedEntity(final EntityType<? extends BlockAttachedEntity> type, final Level level) {
super(type, level);
@@ -92,6 +93,8 @@ public abstract class BlockAttachedEntity extends Entity {
&& (!this.level().mayInteract(player, this.pos) || this.hurtOrSimulate(this.damageSources().playerAttack(player), 0.0F));
}
+ // Paper start - Backport MC-311924 fix
+ /*
@Override
public void kill(final ServerLevel level) {
this.kill(level, this);
@@ -105,6 +108,17 @@ public abstract class BlockAttachedEntity extends Entity {
protected void onKilled() {
}
+ */
+
+ public @Nullable DamageSource getLastDamageSource() {
+ return this.lastDamageSource;
+ }
+
+ protected void kill(final ServerLevel level, final DamageSource source) {
+ this.lastDamageSource = source;
+ this.kill(level);
+ }
+ // Paper end - Backport MC-311924 fix
@Override
public boolean hurtClient(final DamageSource source) {
@@ -149,7 +163,7 @@ public abstract class BlockAttachedEntity extends Entity {
return true;
}
// CraftBukkit end
- this.kill(level, source.getEntity());
+ this.kill(level, source); // Paper - Backport MC-311924 fix
this.markHurt();
this.dropItem(level, source.getEntity());
}
diff --git a/net/minecraft/world/entity/decoration/Cushion.java b/net/minecraft/world/entity/decoration/Cushion.java
index 5766e5564e5047076c5fd326864a1a2c9ba5c8fd..fb1894ac6eb99b60544f6ac2a13de86f045422ef 100644
--- a/net/minecraft/world/entity/decoration/Cushion.java
+++ b/net/minecraft/world/entity/decoration/Cushion.java
@@ -143,7 +143,7 @@ public class Cushion extends BlockAttachedEntity {
@Override
public void thunderHit(final ServerLevel level, final LightningBolt lightningBolt) {
if (!this.isRemoved()) {
- this.kill(level, lightningBolt);
+ this.kill(level, this.damageSources().lightningBolt()); // Paper - Backport MC-311924 fix
this.dropItem(level, lightningBolt);
}
}
diff --git a/net/minecraft/world/entity/decoration/ItemFrame.java b/net/minecraft/world/entity/decoration/ItemFrame.java
index bb8f83b544fba6e7d0db0ca59350e5c924752ffb..f395b070ee0bf866895f02018798ecbfbb80f6c8 100644
--- a/net/minecraft/world/entity/decoration/ItemFrame.java
+++ b/net/minecraft/world/entity/decoration/ItemFrame.java
@@ -158,8 +158,9 @@ public class ItemFrame extends HangingEntity {
}
@Override
- protected void onKilled() {
+ public void kill(final ServerLevel level) { // Paper - Backport MC-311924 fix
this.removeFramedMap(this.getItem());
+ super.kill(level); // Paper - Backport MC-311924 fix
}
private boolean shouldDamageDropItem(final DamageSource source) {
diff --git a/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java b/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java
index 8ad288bfb58d888950c2404ccd287b16d34bf67f..3adfffd3f0a7e6b4867248f6483a36ca9f5f1ee3 100644
--- a/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java
@@ -101,9 +101,21 @@ public class SculkShriekerBlockEntity extends BlockEntity implements GameEventLi
return player;
} else if (sourceEntity instanceof Projectile projectile && projectile.getOwner() instanceof ServerPlayer player) {
return player;
+ // Paper start - Backport MC-311924 fix
+ } else if (sourceEntity instanceof ItemEntity item && item.getOwner() instanceof ServerPlayer player) {
+ return player;
+ } else if (sourceEntity instanceof net.minecraft.world.entity.decoration.BlockAttachedEntity entity
+ && entity.getLastDamageSource() != null
+ && entity.getLastDamageSource().getEntity() instanceof ServerPlayer player) {
+ return player;
} else {
- return sourceEntity instanceof ItemEntity item && item.getOwner() instanceof ServerPlayer player ? player : null;
+ return sourceEntity instanceof net.minecraft.world.entity.decoration.ArmorStand armorStand
+ && armorStand.getLastDamageSource() != null
+ && armorStand.getLastDamageSource().getEntity() instanceof ServerPlayer player
+ ? player
+ : null;
}
+ // Paper end - Backport MC-311924 fix
}
public void tryShriek(final ServerLevel level, final @Nullable ServerPlayer player) {
@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Sun, 4 Oct 2026 08:33:14 +0200
Subject: [PATCH] Fix MC-311788
diff --git a/net/minecraft/world/entity/decoration/ItemFrame.java b/net/minecraft/world/entity/decoration/ItemFrame.java
index f395b070ee0bf866895f02018798ecbfbb80f6c8..1b88b2427d24deae210be9f69da8c5a30e2d08df 100644
--- a/net/minecraft/world/entity/decoration/ItemFrame.java
+++ b/net/minecraft/world/entity/decoration/ItemFrame.java
@@ -102,6 +102,13 @@ public class ItemFrame extends HangingEntity {
this.syncPacketPositionCodec(this.getX(), this.getY(), this.getZ());
}
+ // Paper start - MC-311788 workaround
+ @Override
+ public boolean getRequiresPrecisePosition() {
+ return true; // Fix client bug that applies stale position data on relative moves by forcing absolute positions
+ }
+ // Paper end - MC-311788 workaround
+
@Override
protected AABB calculateBoundingBox(final BlockPos blockPos, final Direction direction) {
return this.createBoundingBox(blockPos, direction, this.hasFramedMap());
@@ -203,12 +203,12 @@
+
+ @Override
+ public int configMinChunkUnloadCount(final ServerLevel world) {
+ return 50;
+ return world.paperConfig().chunks.minChunkUnloadCount;
+ }
+
+ @Override
+ public double configMinChunkUnloadFraction(final ServerLevel world) {
+ return 0.05;
+ return world.paperConfig().chunks.minChunkUnloadFraction;
+ }
+
+ @Override
@@ -1,6 +1,6 @@
--- /dev/null
+++ b/io/papermc/paper/FeatureHooks.java
@@ -1,0 +_,251 @@
@@ -1,0 +_,259 @@
+package io.papermc.paper;
+
+import io.papermc.paper.command.PaperSubcommand;
@@ -248,6 +248,14 @@
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ public static io.papermc.paper.world.chunk.ChunkGenerationState getChunkGenerationState(final net.minecraft.server.level.ServerLevel level, final int chunkX, final int chunkZ) {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ public static java.util.concurrent.CompletableFuture<io.papermc.paper.world.chunk.ChunkGenerationState> getChunkGenerationStateAsync(final net.minecraft.server.level.ServerLevel level, final int chunkX, final int chunkZ) {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ public static void flushAsyncAppenders() {
+ }
+
@@ -4,7 +4,7 @@
import org.slf4j.Logger;
public class Commands {
+ public interface RestrictedMarker { } // Paper - restricted api
+ public interface RestrictedMarker { boolean restricted(); } // Paper - restricted api
public static final String COMMAND_PREFIX = "/";
private static final ThreadLocal<@Nullable ExecutionContext<CommandSourceStack>> CURRENT_EXECUTION_CONTEXT = new ThreadLocal<>();
private static final Logger LOGGER = LogUtils.getLogger();
@@ -12,7 +12,7 @@
@Override
public boolean isRestricted(final CommandNode<CommandSourceStack> node) {
+ if (node.getRequirement() instanceof RestrictedMarker) return true; // Paper - restricted api
+ if (node.getRequirement() instanceof RestrictedMarker marker) return marker.restricted(); // Paper - restricted api
Predicate<CommandSourceStack> requirement = node.getRequirement();
return !requirement.test(this.noPermissionSource);
}
@@ -139,7 +139,7 @@
}
return null;
@@ -415,19 +_,112 @@
@@ -415,10 +_,65 @@
}
public void sendCommands(final ServerPlayer player) {
@@ -152,9 +152,10 @@
+ // CraftBukkit start
+ // Register Vanilla commands into builtRoot as before
+ // Paper start - Perf: Async command map building
+ // Copy root children to avoid concurrent modification during building
+ final java.util.Collection<CommandNode<CommandSourceStack>> commandNodes = new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren());
+ COMMAND_SENDING_POOL.execute(() -> this.sendAsync(player, commandNodes));
+ // Copy root node to avoid concurrent modification during building
+ final RootCommandNode<CommandSourceStack> rootNode = new RootCommandNode<>();
+ this.dispatcher.getRoot().getChildren().forEach(rootNode::addChild);
+ COMMAND_SENDING_POOL.execute(() -> this.sendAsync(player, rootNode));
+ }
+
+ // Fixed pool, but with discard policy
@@ -168,20 +169,23 @@
+ new java.util.concurrent.ThreadPoolExecutor.DiscardPolicy()
+ );
+
+ private void sendAsync(ServerPlayer player, java.util.Collection<CommandNode<CommandSourceStack>> dispatcherRootChildren) {
+ private void sendAsync(ServerPlayer player, RootCommandNode<CommandSourceStack> dispatcherRoot) {
+ // Paper end - Perf: Async command map building
Map<CommandNode<CommandSourceStack>, CommandNode<CommandSourceStack>> playerCommands = new HashMap<>();
RootCommandNode<CommandSourceStack> root = new RootCommandNode<>();
playerCommands.put(this.dispatcher.getRoot(), root);
- playerCommands.put(this.dispatcher.getRoot(), root);
- fillUsableCommands(this.dispatcher.getRoot(), root, player.createCommandSourceStack(), playerCommands);
+ fillUsableCommands(dispatcherRootChildren, root, player.createCommandSourceStack(), playerCommands); // Paper - Perf: Async command map building; pass copy of children
+ // Paper start - Perf: Async command map building; pass copy of root
+ playerCommands.put(dispatcherRoot, root);
+ fillUsableCommands(dispatcherRoot, root, player.createCommandSourceStack(), playerCommands);
+ // Paper end - Perf: Async command map building; pass copy of root
+
+ java.util.Collection<String> bukkit = new java.util.LinkedHashSet<>();
+ for (CommandNode node : root.getChildren()) {
+ for (CommandNode<?> node : root.getChildren()) {
+ bukkit.add(node.getName());
+ }
+ // Paper start - Perf: Async command map building
+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) root, false).callEvent(); // Paper - Brigadier API
+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<>(player.getBukkitEntity(), root, false).callEvent(); // Paper - Brigadier API
+ net.minecraft.server.MinecraftServer.getServer().execute(() -> {
+ runSync(player, bukkit, root);
+ });
@@ -189,7 +193,7 @@
+
+ private void runSync(ServerPlayer player, java.util.Collection<String> bukkit, RootCommandNode<CommandSourceStack> root) {
+ // Paper end - Perf: Async command map building
+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) root, true).callEvent(); // Paper - Brigadier API
+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<>(player.getBukkitEntity(), root, true).callEvent(); // Paper - Brigadier API
+ org.bukkit.event.player.PlayerCommandSendEvent event = new org.bukkit.event.player.PlayerCommandSendEvent(player.getBukkitEntity(), new java.util.LinkedHashSet<>(bukkit));
+ event.getPlayer().getServer().getPluginManager().callEvent(event);
+
@@ -203,12 +207,10 @@
player.connection.send(new ClientboundCommandsPacket(root, COMMAND_NODE_INSPECTOR));
}
private static <S> void fillUsableCommands(
- final CommandNode<S> source, final CommandNode<S> target, final S commandFilter, final Map<CommandNode<S>, CommandNode<S>> converted
+ final java.util.Collection<CommandNode<S>> children, final CommandNode<S> target, final S commandFilter, final Map<CommandNode<S>, CommandNode<S>> converted // Paper - Perf: Async command map building; pass copy of children
@@ -426,8 +_,49 @@
final CommandNode<S> source, final CommandNode<S> target, final S commandFilter, final Map<CommandNode<S>, CommandNode<S>> converted
) {
- for (CommandNode<S> child : source.getChildren()) {
+ for (CommandNode<S> child : children) { // Paper - Perf: Async command map building; pass copy of children
for (CommandNode<S> child : source.getChildren()) {
+ // Paper start - Brigadier API
+ if (child.clientNode != null) {
+ child = child.clientNode;
@@ -255,12 +257,3 @@
if (builder.getRedirect() != null) {
builder.redirect(converted.get(builder.getRedirect()));
}
@@ -436,7 +_,7 @@
converted.put(child, node);
target.addChild(node);
if (!child.getChildren().isEmpty()) {
- fillUsableCommands(child, node, commandFilter, converted);
+ fillUsableCommands(child.getChildren(), node, commandFilter, converted); // Paper - Perf: Async command map building; pass copy of children
}
}
}
@@ -65,7 +65,7 @@
if (this.delayedDisconnect != null) {
this.disconnect(this.delayedDisconnect);
}
@@ -100,15 +_,32 @@
@@ -100,15 +_,45 @@
}
@Override
@@ -87,6 +87,19 @@
+ }
+ // Paper end - Handle large packets disconnecting client
if (cause instanceof SkipPacketException) {
+ // Paper start - packet limiter
+ if (cause instanceof SkipPacketDecoderException && !this.stopReadingPackets) {
+ this.receivedPackets++;
+ if (this.allPacketCounts != null) {
+ synchronized (this.PACKET_LIMIT_LOCK) {
+ this.allPacketCounts.updateAndAdd(1, System.nanoTime());
+ if (this.allPacketCounts.getRate() >= io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.maxPacketRate()) {
+ this.killForPacketSpam();
+ }
+ }
+ }
+ }
+ // Paper end - packet limiter
LOGGER.debug("Skipping packet due to errors", cause.getCause());
} else {
boolean isFirstFault = !this.handlingFault;
@@ -319,7 +332,7 @@
+ /* Player was logged in, either game listener or configuration listener */
+ final com.mojang.authlib.GameProfile profile = commonPacketListener.getOwner();
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.id(),
+ profile.name(), ((InetSocketAddress) this.address).getAddress(), false).callEvent();
+ profile.name(), this.address, false).callEvent();
+ } else if (packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginListener) {
+ /* Player is login stage */
+ switch (loginListener.state) {
@@ -329,7 +342,7 @@
+ case ACCEPTED:
+ final com.mojang.authlib.GameProfile profile = loginListener.authenticatedProfile; /* Should be non-null at this stage */
+ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.id(), profile.name(),
+ ((InetSocketAddress) this.address).getAddress(), false).callEvent();
+ this.address, false).callEvent();
+ }
+ }
+ // Paper end - Add PlayerConnectionCloseEvent
Loaded 100 of 303 files, more files were not shown because too many files have changed in this diff. Show more