Compare commits

...
Author SHA1 Message Date
Jake Potrebic 68f8144273 fix javadoc 2026-11-03 15:19:18 -08:00
Jake Potrebic f71c974030 Update deprecated since msgs 2026-11-03 15:02:44 -08:00
Jake Potrebic adadeb086d Merge remote-tracking branch 'upstream/main' into feature/26.3-holdersets 2026-11-03 14:37:38 -08:00
Pedro df120fd8b5 Add OperatorFeaturesAllowed API for Signs (#14451) 2026-11-02 20:55:58 -03:00
Jason Penilla 7e88a0da38 Sync latest Moonrise changes (#14452) 2026-11-01 20:07:58 +01:00
Bjarne Koll f11902627b Correct data counts for custom inventories (#14449)
Update the hardcoded data counts in CraftContainer to reference the constants used by the menus themselves.
2026-11-01 12:09:17 +00:00
ClexusandWarrior bb10cf0d5b Add EntityLandEvent (#14204)
Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
2026-10-31 11:17:23 -03:00
metabrix 55bebded5f Parse pasted multi-line commands separately line by line (#13007) 2026-10-31 10:45:30 -03:00
Michał Fox f728a121f7 Fix respawn radius only covering part of the square (#14390) 2026-10-31 10:31:53 -03:00
balugaq 2401035c89 Add missing ensureSize in MaterialTags (#14438) 2026-10-31 13:27:55 +00:00
Daksh Rawat 174574d871 [ci skip] Fix RedundantNullabilityCheck for nested types (#14430) 2026-10-30 09:15:58 -03:00
Jake Potrebic 8a170a38c6 Update checkstyle (#14431) 2026-10-29 18:53:15 -07:00
Shane Freeder 804dd0c3f0 Centralise TreeType <-> tree feature mapping and test it against TreeGrower (#14417) 2026-10-29 11:28:37 +00:00
Tamion ff31a2c997 Fix cancelled PotionSplashEvent putting out fires (#12194) 2026-10-29 09:29:38 +01:00
Lulu13022002 2e549d651c Call EntityDamageByBlockEvent for fire damage (#12235) 2026-10-29 09:14:15 +01:00
7f9c79794e Fix duplicate dispense events for some items (#13922)
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
Co-authored-by: Bjarne Koll <git@lynxplay.dev>
2026-10-27 19:22:34 +00:00
Daksh Rawat 9d950517d9 [ci skip] Fix JavadocAlignParameterDescriptionCheck with inline tags (#14427) 2026-10-27 16:27:57 +01:00
Marlon Pohl 4f4904928f Add API to query type of PersistentDataContainer value (#14143) 2026-10-26 22:03:51 +01:00
TiggerTakkyTTT 4623163095 Detect rebound middle click clone actions in survival (#14249) 2026-10-26 21:54:48 +01:00
Johan-HynellandWarrior eb9098a546 Made time format output prefix for command block configurable (#14322)
Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
2026-10-26 11:18:17 -03:00
Cubicake e415f5e030 Don't refresh entity data if its removed (#14423) 2026-10-25 09:46:40 -03:00
Creeam e3ba7b1dc8 Regen missing worldgen settings file from server.properties (#14233) 2026-10-24 10:18:07 -03:00
metabrix 05c2798ec0 Fix cancelled running scheduled task state (#14109) 2026-10-24 10:13:14 -03:00
TiggerTakkyTTTandDoc 02378ab582 Fire PrepareItemCraftEvent for Crafter preview (#14320)
Co-authored-by: Doc <git@mrdoc.dev>
2026-10-24 09:52:52 -03:00
Warrior 67912d0827 [ci skip] Re-enable using JBR for local run tasks again (#14318) 2026-10-24 09:13:46 -03:00
personalized-advertising e8a1f8fd68 Configurable plugin message channel limit (#14160) 2026-10-24 09:07:27 -03:00
personalized-advertising 1b9bf5c8e9 Add EntityPotentSulfurLaunchEvent (#14237) 2026-10-23 10:47:23 -03:00
Marco N. 325f9071f0 Clear scoreboard team entries on unregister (#14159) 2026-10-23 10:21:43 -03:00
PedroandWarrior d86d427405 Call PlayerBucketEmptyEvent for Sulfur Cube in Bucket (#14150)
Co-authored-by: Warrior <50800980+Warriorrrr@users.noreply.github.com>
2026-10-23 09:45:41 -03:00
Gotze 2f1ce01e87 Improve swing hand API to respect item ATTACK_ANIMATION component (#14363) 2026-10-23 09:38:43 -03:00
Bjarne Koll 0d9bf08a23 [ci skip] Fix broken test plugin refs 2026-10-22 18:10:53 +01:00
Mickey42302 e65c0765c6 Add permissions for reset and sequence arguments in /random (#14128) 2026-10-22 15:30:22 +02:00
H. Boyd ac9a1b2c72 Make Color implement adventure ARGBLike (#14349) 2026-10-22 09:59:01 -03:00
Strokkur24andJason Penilla 0db5c93a3d Add helper methods for vanilla-style command feedback (#14331)
Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
2026-10-22 09:54:34 -03:00
Shane Freeder 2d602d68c1 Split TreeType.POPLAR into per-color poplar tree types (#14416) 2026-10-21 14:12:43 +01:00
TiggerTakkyTTT 2766dd30b1 Add API for explorer maps using specific structures (#14290) 2026-10-21 09:31:41 -03:00
Warrior 03af8b4291 Fix shutdown thread deadlock if it tries to close async (#14410) 2026-10-20 21:49:26 +00:00
Jason Penilla 45fac22871 Sync latest Moonrise changes (#14409) 2026-10-20 21:41:39 +00: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
Lulu13022002 c2f3a0311a reduce verbosity of RegistryKeySet and expose fixed range on Sound 2026-09-12 20:38:49 +02:00
Lulu13022002 9ae07284ad add some convenient methods to TooltipDisplay and matchesWithoutData 2026-08-21 17:03:51 +02:00
Lulu13022002 6fcbe1b7c3 port remaining changes 2026-08-19 16:31:04 +02:00
Jake Potrebic 426ee4eeda Support proper inlined sound events 2026-08-09 23:29:57 +02:00
Lulu13022002 db3bf3cb24 only attempt to import registry entry class once 2026-08-09 15:52:57 +02:00
Jake Potrebic 6c4ddf5f84 Clarify registry layers 2026-08-08 15:00:49 -07:00
Jake Potrebic 86f056a018 Simplify registry event registration 2026-08-08 14:30:23 -07:00
Lulu13022002 04beece4d9 [ci skip] format 2026-08-08 22:09:40 +02:00
Jake Potrebic 40af9fc7fe Resolve sulfur cube issue 2026-08-08 12:03:19 -07:00
Lulu13022002 4461b2642b TypedKey is not a Key 2026-08-08 19:52:21 +02:00
Lulu13022002 4ee96debfd merge registry element api 2026-08-08 19:37:49 +02:00
Jake Potrebic 644064cdd1 Update for 26.3 holder sets 2026-08-06 22:22:36 -07:00
485 changed files with 12905 additions and 3894 deletions

No files matched your search

+25 -2
View File
@@ -14,11 +14,18 @@
<property name="messageKey" value="annotation\.same\.line"/>
<property name="arguments" value="^(?!(${type_use_annotations}))"/>
</module>
<module name="io.papermc.checkstyle.filters.BetterSuppressionSingleFilter">
<!--Allows @deprecated on packages. Not in the javadoc spec (javadoc warns), but it is still rendered in the deprecation block and list-->
<property name="checks" value="InappropriateJavadocBlockTagsOnPackage"/>
<property name="messageKey" value="javadoc\.inappropriate\.tag"/>
<property name="arguments" value="^deprecated$"/>
</module>
<module name="SuppressWarningsFilter"/>
<!--Javadoc Comments-->
<module name="JavadocPackage"/>
<!--Misc-->
<module name="LineEnding"/>
<module name="NewlineAtEndOfFile"/>
<!--Whitespace-->
<module name="FileTabCharacter"/>
@@ -38,6 +45,7 @@
</module>
<module name="AnnotationUseStyle"/>
<module name="MissingDeprecated"/>
<module name="MissingOverrideOnRecordAccessor"/>
<!--Block Checks-->
<module name="AvoidNestedBlocks"/>
@@ -55,7 +63,9 @@
<!--Coding-->
<module name="AvoidDoubleBraceInitialization"/>
<module name="AvoidNoArgumentSuperConstructorCall"/>
<module name="ConstructorsDeclarationGrouping"/>
<module name="ConstructorsDeclarationGrouping">
<property name="orderByIncreasingParameterCount" value="true"/>
</module>
<module name="CovariantEquals"/>
<module name="DeclarationOrder">
<property name="ignoreConstructors" value="true"/>
@@ -63,6 +73,7 @@
<module name="DefaultComesLast"/>
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="ExpressionOverBlockLambda"/>
<module name="FallThrough"/>
<!-- Use non-final variables as an identifier of old code for now -->
<!--<module name="FinalLocalVariable">
@@ -86,6 +97,9 @@
<module name="UnnecessarySemicolonInTryWithResources"/>
<module name="UnusedCatchParameterShouldBeUnnamed"/>
<module name="UnusedLocalVariable"/>
<module name="UnusedPrivateField"/>
<module name="UnusedTryResourceShouldBeUnnamed"/>
<module name="UseEnhancedSwitch"/>
<module name="WhenShouldBeUsed"/>
<!--Headers--> <!--N/A-->
@@ -114,9 +128,13 @@
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @see, @deprecated, @hidden"/>
</module>
<module name="InappropriateJavadocBlockTagsOnField"/>
<module name="InappropriateJavadocBlockTagsOnPackage"/>
<module name="InappropriateJavadocBlockTagsOnType"/>
<module name="InvalidJavadocPosition"/>
<module name="JavadocBlockTagLocation"/>
<module name="JavadocContentLocation"/>
<module name="JavadocEndCommentDelimiter"/>
<module name="JavadocLeadingAsteriskAlign"/>
<module name="JavadocMethod">
<!--checks all, but doesn't require. If we have a doc, it should be valid-->
@@ -124,11 +142,11 @@
</module>
<module name="JavadocMissingLeadingAsterisk"/>
<module name="JavadocMissingWhitespaceAfterAsterisk"/>
<module name="JavadocStyle"/> <!--checks all, but doesn't require. If we have a doc, it should be valid-->
<module name="JavadocTagContinuationIndentation"/>
<module name="JavadocType"/> <!--checks all, but doesn't require. If we have a doc, it should be valid-->
<module name="NonEmptyAtclauseDescription"/>
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
<module name="SummaryJavadoc"/> <!--checks all, but doesn't require. If we have a doc, it should be valid-->
<!--Metrics--> <!--N/A-->
@@ -136,12 +154,14 @@
<module name="ArrayTypeStyle"/>
<module name="AvoidEscapedUnicodeCharacters"/>
<module name="CommentsIndentation"/>
<module name="HexLiteralCase"/>
<!-- Overlaps with the configured FinalLocalVariable, but is more strict -->
<!--<module name="FinalParameters">
<property name="tokens" value="METHOD_DEF,CTOR_DEF,LITERAL_CATCH,FOR_EACH_CLAUSE,PATTERN_VARIABLE_DEF"/>
</module>-->
<module name="NoCodeInFile"/>
<module name="NumericalPrefixesInfixesSuffixesCharacterCase"/>
<module name="OuterTypeFilename"/>
<module name="UpperEll"/>
@@ -180,6 +200,7 @@
<!--Size Violations--> <!--N/A-->
<!--Whitespace-->
<module name="ArrayBracketNoWhitespace"/>
<module name="EmptyForInitializerPad"/>
<module name="EmptyForIteratorPad"/>
<module name="EmptyLineSeparator">
@@ -197,9 +218,11 @@
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<module name="WhitespaceBeforeEmptyBody"/>
<!--Custom-->
<module name="JavadocAlignParameterDescription"/>
<module name="JavadocParamTagOrder"/>
<module name="NullabilityAnnotations"/>
<module name="RedundantNullability"/>
<module name="UnnecessaryFullyQualifiedImport"/>
+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.
+8
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
@@ -472,8 +477,10 @@ public net.minecraft.world.inventory.AbstractMountInventoryMenu mount
public net.minecraft.world.inventory.AnvilMenu cost
public net.minecraft.world.inventory.AnvilMenu itemName
public net.minecraft.world.inventory.AnvilMenu repairItemCountCost
public net.minecraft.world.inventory.BrewingStandMenu DATA_COUNT
public net.minecraft.world.inventory.BrewingStandMenu brewingStandData
public net.minecraft.world.inventory.CraftingMenu access
public net.minecraft.world.inventory.LecternMenu DATA_COUNT
public net.minecraft.world.inventory.MerchantContainer selectionHint
public net.minecraft.world.item.AdventureModePredicate predicates
public net.minecraft.world.item.CrossbowItem FIREWORK_POWER
@@ -578,6 +585,7 @@ public net.minecraft.world.level.block.entity.LecternBlockEntity bookAccess
public net.minecraft.world.level.block.entity.LecternBlockEntity setPage(I)V
public net.minecraft.world.level.block.entity.SculkCatalystBlockEntity$CatalystListener bloom(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/util/RandomSource;)V
public net.minecraft.world.level.block.entity.SculkShriekerBlockEntity warningLevel
public net.minecraft.world.level.block.entity.SignBlockEntity allowOpFeatures
public net.minecraft.world.level.block.entity.SkullBlockEntity customName
public net.minecraft.world.level.block.entity.SkullBlockEntity noteBlockSound
public net.minecraft.world.level.block.entity.SkullBlockEntity owner
+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
@@ -234,7 +234,8 @@ public class MaterialTags {
* Covers all music disc items.
*/
public static final MaterialSetTag MUSIC_DISCS = new MaterialSetTag(keyFor("music_discs"))
.startsWith("MUSIC_DISC_").lock();
.startsWith("MUSIC_DISC_")
.ensureSize("MUSIC_DISCS", 22).lock();
/**
* Covers all ores.
@@ -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);
}
@@ -1,9 +1,11 @@
package io.papermc.paper.block.pot;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.data.DecoratedPotPatternRegistryEntry;
import org.bukkit.Keyed;
/**
* A pot pattern on a decorated pot.
*/
public interface PotPatternType extends Keyed {
public interface PotPatternType extends RegistryElement.Buildable<PotPatternType, DecoratedPotPatternRegistryEntry, DecoratedPotPatternRegistryEntry.Builder>, Keyed {
}
@@ -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.
@@ -1,8 +1,14 @@
package io.papermc.paper.command.brigadier;
import com.google.common.base.Preconditions;
import com.mojang.brigadier.RedirectModifier;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.CommandNode;
import net.kyori.adventure.pointer.Pointered;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.GameRules;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
@@ -81,4 +87,123 @@ public interface CommandSourceStack {
* @see com.mojang.brigadier.builder.ArgumentBuilder#fork(CommandNode, RedirectModifier)
*/
CommandSourceStack withExecutor(Entity executor);
/**
* Sends a system message to the {@link #getExecutor()} if it is a {@link Player},
* otherwise sends a system message to the {@link #getSender()}.
*
* @param message the message to send
* @throws IllegalArgumentException if the message is null
*/
void sendReply(ComponentLike message);
/**
* Sends a system message with the MiniMessage format to the {@link #getExecutor()}
* if it is a {@link Player}, otherwise sends it to the {@link #getSender()}.
*
* <p>See <a href="https://docs.papermc.io/adventure/minimessage/">MiniMessage docs</a> and
* <a href="https://docs.papermc.io/adventure/minimessage/dynamic-replacements/">MiniMessage Placeholders docs</a>
* for more information on the format.</p>
*
* <p>This method provides the target of the system message as a {@link net.kyori.adventure.pointer.Pointered}
* to the MiniMessage {@link MiniMessage#deserialize(String, Pointered, TagResolver...)} method.</p>
*
* @param message the MiniMessage message to send
* @param resolvers resolvers to use
* @throws IllegalArgumentException if the message is null
*/
default void sendRichReply(final String message, final TagResolver... resolvers) {
Preconditions.checkArgument(message != null, "message cannot be null.");
this.sendReply(MiniMessage.miniMessage().deserialize(
message,
this.getExecutor() instanceof final Player player ? player : this.getSender(),
resolvers
));
}
/**
* Sends a system message to the {@link #getSender()}, admins, and console indicating successful command execution
* according to vanilla semantics.
*
* <p>This currently includes checking for environments with suppressed output,
* {@link GameRules#SEND_COMMAND_FEEDBACK}, and {@link GameRules#LOG_ADMIN_COMMANDS}.</p>
*
* @param message the message to send
* @param allowInformingAdmins whether admins and console may be informed of this success
* @throws IllegalArgumentException if the message is null
*/
void sendSuccess(ComponentLike message, boolean allowInformingAdmins);
/**
* Sends a system message to the {@link #getSender()}, admins, and console indicating successful command execution
* according to vanilla semantics. This method informs admins and the console of this success.
*
* <p>This currently includes checking for environments with suppressed output,
* {@link GameRules#SEND_COMMAND_FEEDBACK}, and {@link GameRules#LOG_ADMIN_COMMANDS}.</p>
*
* @param message the message to send
* @see #sendSuccess(ComponentLike, boolean) to disable if admins and console should be informed
* @throws IllegalArgumentException if the message is null
*/
default void sendSuccess(final ComponentLike message) {
this.sendSuccess(message, true);
}
/**
* Sends a system message with the MiniMessage format to the {@link #getSender()}, admins, and console indicating
* successful command execution according to vanilla semantics. This method informs admins and the console of
* this success.
*
* <p>This currently includes checking for environments with suppressed output,
* {@link GameRules#SEND_COMMAND_FEEDBACK}, and {@link GameRules#LOG_ADMIN_COMMANDS}.</p>
*
* <p>See <a href="https://docs.papermc.io/adventure/minimessage/">MiniMessage docs</a> and
* <a href="https://docs.papermc.io/adventure/minimessage/dynamic-replacements/">MiniMessage Placeholders docs</a>
* for more information on the format.</p>
*
* <p>This method provides the {@link CommandSender} of this command as a {@link net.kyori.adventure.pointer.Pointered}
* to the MiniMessage {@link MiniMessage#deserialize(String, Pointered, TagResolver...)} method.</p>
*
* @param message the MiniMessage message to send
* @param resolvers resolvers to use
* @see #sendSuccess(ComponentLike, boolean) to disable if admins and console should be informed
* @throws IllegalArgumentException if the message is null
*/
default void sendRichSuccess(final String message, final TagResolver... resolvers) {
Preconditions.checkArgument(message != null, "message cannot be null.");
this.sendSuccess(MiniMessage.miniMessage().deserialize(message, this.getSender(), resolvers), true);
}
/**
* Sends a system message indicating a failed command execution to the {@link #getSender()}.
* Does not apply red styling to the message as vanilla does to allow for custom failure message styling.
*
* <p>Respects vanilla semantics for accepting failure output and suppressed output environments.</p>
*
* @param message the message to send
* @throws IllegalArgumentException if the message is null
*/
void sendFailure(ComponentLike message);
/**
* Sends a system message with the MiniMessage format indicating a failed command execution to the{@link #getSender()}.
* Does not apply red styling to the message as vanilla does to allow for custom failure message styling.
*
* <p>Respects vanilla semantics for accepting failure output and suppressed output environments.</p>
*
* <p>See <a href="https://docs.papermc.io/adventure/minimessage/">MiniMessage docs</a> and
* <a href="https://docs.papermc.io/adventure/minimessage/dynamic-replacements/">MiniMessage Placeholders docs</a>
* for more information on the format.</p>
*
* <p>This method provides the {@link CommandSender} of this command as a {@link net.kyori.adventure.pointer.Pointered}
* to the MiniMessage {@link MiniMessage#deserialize(String, Pointered, TagResolver...)} method.</p>
*
* @param message the MiniMessage message to send
* @param resolvers resolvers to use
* @throws IllegalArgumentException if the message is null
*/
default void sendRichFailure(final String message, final TagResolver... resolvers) {
Preconditions.checkArgument(message != null, "message cannot be null.");
this.sendFailure(MiniMessage.miniMessage().deserialize(message, this.getSender(), resolvers));
}
}
@@ -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}
*/
@@ -1,12 +1,13 @@
package io.papermc.paper.datacomponent;
import io.papermc.paper.registry.RegistryElement;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
@NullMarked
@ApiStatus.NonExtendable
public interface DataComponentType extends Keyed {
public interface DataComponentType extends RegistryElement<DataComponentType>, Keyed {
/**
* Checks if this data component type is persistent, or
@@ -60,6 +60,7 @@ import org.bukkit.DyeColor;
import org.bukkit.FireworkEffect;
import org.bukkit.MusicInstrument;
import org.bukkit.Registry;
import org.bukkit.Sound;
import org.bukkit.block.banner.PatternType;
import org.bukkit.damage.DamageType;
import org.bukkit.entity.Axolotl;
@@ -365,7 +366,7 @@ public final class DataComponentTypes {
* Holds the unresolved loot table and seed of a container-like block.
*/
public static final DataComponentType.Valued<SeededContainerLoot> CONTAINER_LOOT = valued("container_loot");
public static final DataComponentType.Valued<Key> BREAK_SOUND = valued("break_sound");
public static final DataComponentType.Valued<Sound> BREAK_SOUND = valued("break_sound");
public static final DataComponentType.Valued<MobVisibility> MOB_VISIBILITY = valued("mob_visibility");
public static final DataComponentType.Valued<Villager.Type> VILLAGER_VARIANT = valued("villager/variant");
public static final DataComponentType.Valued<Wolf.Variant> WOLF_VARIANT = valued("wolf/variant");
@@ -6,6 +6,7 @@ import io.papermc.paper.datacomponent.item.blocksattacks.ItemDamageFunction;
import io.papermc.paper.registry.set.RegistryKeySet;
import java.util.List;
import net.kyori.adventure.key.Key;
import org.bukkit.Sound;
import org.bukkit.damage.DamageType;
import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.ApiStatus;
@@ -68,20 +69,20 @@ public interface BlocksAttacks {
@Nullable RegistryKeySet<DamageType> bypassedBy();
/**
* Gets the key sound to play when an attack is successfully blocked.
* Gets the sound to play when an attack is successfully blocked.
*
* @return a key of the sound
* @return the sound
*/
@Contract(pure = true)
@Nullable Key blockSound();
@Nullable Sound blockSound();
/**
* Gets the key sound to play when the item goes on its disabled cooldown due to an attack.
* Gets the sound to play when the item goes on its disabled cooldown due to an attack.
*
* @return a key of the sound
* @return the sound
*/
@Contract(pure = true)
@Nullable Key disableSound();
@Nullable Sound disableSound();
/**
* Builder for {@link BlocksAttacks}.
@@ -107,10 +108,26 @@ public interface BlocksAttacks {
@Contract(value = "_ -> this", mutates = "this")
Builder bypassedBy(@Nullable RegistryKeySet<DamageType> bypassedBy);
/**
* Sets the sound to play when an attack is successfully blocked.
*
* @param sound the sound, or null to play no sound
* @return the builder for chaining
* @see Sound#create(net.kyori.adventure.key.Key, Float)
* @see Sound#create(java.util.function.Consumer)
*/
@Contract(value = "_ -> this", mutates = "this")
Builder blockSound(@Nullable Key sound);
Builder blockSound(@Nullable Sound sound);
/**
* Sets the sound to play when the item goes on its disabled cooldown due to an attack.
*
* @param sound the sound, or null to play no sound
* @return the builder for chaining
* @see Sound#create(net.kyori.adventure.key.Key, Float)
* @see Sound#create(java.util.function.Consumer)
*/
@Contract(value = "_ -> this", mutates = "this")
Builder disableSound(@Nullable Key sound);
Builder disableSound(@Nullable Sound sound);
}
}
@@ -6,6 +6,7 @@ import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
import java.util.List;
import net.kyori.adventure.key.Key;
import org.bukkit.Sound;
import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
@@ -30,7 +31,7 @@ public interface Consumable extends BuildableDataComponent<Consumable, Consumabl
ItemUseAnimation animation();
@Contract(pure = true)
Key sound();
Sound sound();
@Contract(pure = true)
boolean hasConsumeParticles();
@@ -65,11 +66,11 @@ public interface Consumable extends BuildableDataComponent<Consumable, Consumabl
/**
* Sets the sound played when consuming the item.
*
* @param sound the {@link Key} representing the sound to be used
* @param sound the sound to be used
* @return the builder for chaining
*/
@Contract(value = "_ -> this", mutates = "this")
Builder sound(Key sound);
Builder sound(Sound sound);
/**
* Sets whether consuming the item results in particle effects.
@@ -4,6 +4,7 @@ import io.papermc.paper.datacomponent.BuildableDataComponent;
import io.papermc.paper.datacomponent.DataComponentBuilder;
import io.papermc.paper.registry.set.RegistryKeySet;
import net.kyori.adventure.key.Key;
import org.bukkit.Sound;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.EquipmentSlot;
import org.jetbrains.annotations.ApiStatus;
@@ -37,12 +38,12 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
EquipmentSlot slot();
/**
* Gets the equip sound key.
* Gets the equip sound.
*
* @return the equip sound key
* @return the equip sound
*/
@Contract(pure = true)
Key equipSound();
Sound equipSound();
/**
* Gets the asset id if present.
@@ -115,7 +116,7 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
* @return shear sound
*/
@Contract(pure = true)
Key shearSound();
Sound shearSound();
/**
* Builder for {@link Equippable}.
@@ -124,13 +125,13 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
interface Builder extends DataComponentBuilder<Equippable> {
/**
* Sets the equip sound key for this item.
* Sets the equip sound for this item.
*
* @param sound the equip sound key
* @param sound the equip sound
* @return the builder for chaining
*/
@Contract(value = "_ -> this", mutates = "this")
Builder equipSound(Key sound);
Builder equipSound(Sound sound);
/**
* Sets the asset id for this item.
@@ -207,10 +208,10 @@ public interface Equippable extends BuildableDataComponent<Equippable, Equippabl
/**
* Sets the sound that is played when shearing this equipment off an entity.
*
* @param shearSound the shear sound key
* @param shearSound the shear sound
* @return the builder for chaining
*/
@Contract(value = "_ -> this", mutates = "this")
Builder shearSound(Key shearSound);
Builder shearSound(Sound shearSound);
}
}
@@ -2,6 +2,7 @@ package io.papermc.paper.datacomponent.item;
import io.papermc.paper.datacomponent.DataComponentBuilder;
import net.kyori.adventure.key.Key;
import org.bukkit.Sound;
import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
@@ -48,10 +49,10 @@ public interface KineticWeapon {
float damageMultiplier();
@Contract(pure = true)
@Nullable Key sound();
@Nullable Sound sound();
@Contract(pure = true)
@Nullable Key hitSound();
@Nullable Sound hitSound();
interface Condition {
@@ -92,10 +93,26 @@ public interface KineticWeapon {
@Contract(value = "_ -> this", mutates = "this")
Builder damageMultiplier(float damageMultiplier);
/**
* Sets the sound to play while this weapon is active.
*
* @param sound the sound, or null to play no sound
* @return the builder for chaining
* @see Sound#create(net.kyori.adventure.key.Key, Float)
* @see Sound#create(java.util.function.Consumer)
*/
@Contract(value = "_ -> this", mutates = "this")
Builder sound(@Nullable Key sound);
Builder sound(@Nullable Sound sound);
/**
* Sets the sound to play when this weapon hits an entity.
*
* @param sound the sound, or null to play no sound
* @return the builder for chaining
* @see Sound#create(net.kyori.adventure.key.Key, Float)
* @see Sound#create(java.util.function.Consumer)
*/
@Contract(value = "_ -> this", mutates = "this")
Builder hitSound(@Nullable Key sound);
Builder hitSound(@Nullable Sound sound);
}
}
@@ -2,6 +2,7 @@ package io.papermc.paper.datacomponent.item;
import io.papermc.paper.datacomponent.DataComponentBuilder;
import net.kyori.adventure.key.Key;
import org.bukkit.Sound;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.Nullable;
@@ -22,9 +23,9 @@ public interface PiercingWeapon {
boolean dismounts();
@Nullable Key sound();
@Nullable Sound sound();
@Nullable Key hitSound();
@Nullable Sound hitSound();
/**
* Builder for {@link PiercingWeapon}.
@@ -38,10 +39,26 @@ public interface PiercingWeapon {
@Contract(value = "_ -> this", mutates = "this")
Builder dismounts(boolean dismounts);
/**
* Sets the sound to play while this weapon is active.
*
* @param sound the sound, or null to play no sound
* @return the builder for chaining
* @see Sound#create(net.kyori.adventure.key.Key, Float)
* @see Sound#create(java.util.function.Consumer)
*/
@Contract(value = "_ -> this", mutates = "this")
Builder sound(@Nullable Key sound);
Builder sound(@Nullable Sound sound);
/**
* Sets the sound to play when this weapon hits an entity.
*
* @param sound the sound, or null to play no sound
* @return the builder for chaining
* @see Sound#create(net.kyori.adventure.key.Key, Float)
* @see Sound#create(java.util.function.Consumer)
*/
@Contract(value = "_ -> this", mutates = "this")
Builder hitSound(@Nullable Key sound);
Builder hitSound(@Nullable Sound sound);
}
}
@@ -2,6 +2,7 @@ package io.papermc.paper.datacomponent.item;
import io.papermc.paper.datacomponent.DataComponentBuilder;
import io.papermc.paper.datacomponent.DataComponentType;
import io.papermc.paper.registry.set.RegistryKeySet;
import java.util.Set;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
@@ -37,5 +38,8 @@ public interface TooltipDisplay {
@Contract(value = "_ -> this", mutates = "this")
Builder hiddenComponents(Set<DataComponentType> components);
@Contract(value = "_ -> this", mutates = "this")
Builder hiddenComponents(RegistryKeySet<DataComponentType> components);
}
}
@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Optional;
import java.util.ServiceLoader;
import net.kyori.adventure.key.Key;
import org.bukkit.Sound;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
@@ -22,7 +23,7 @@ interface ConsumableTypesBridge {
ConsumeEffect.ClearAllStatusEffects clearAllStatusEffects();
ConsumeEffect.PlaySound playSoundEffect(Key sound);
ConsumeEffect.PlaySound playSoundEffect(Sound sound);
ConsumeEffect.TeleportRandomly teleportRandomlyEffect(float diameter, boolean directionalParticles);
}
@@ -3,6 +3,7 @@ package io.papermc.paper.datacomponent.item.consumable;
import io.papermc.paper.registry.set.RegistryKeySet;
import java.util.List;
import net.kyori.adventure.key.Key;
import org.bukkit.Sound;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.ApiStatus;
@@ -52,12 +53,12 @@ public interface ConsumeEffect {
/**
* Creates a consume effect that plays a sound on consumption.
*
* @param key the key sound effect to play
* @param sound the sound effect to play
* @return the effect instance
*/
@Contract(value = "_ -> new", pure = true)
static PlaySound playSoundConsumeEffect(final Key key) {
return ConsumableTypesBridge.bridge().playSoundEffect(key);
static PlaySound playSoundConsumeEffect(final Sound sound) {
return ConsumableTypesBridge.bridge().playSoundEffect(sound);
}
/**
@@ -128,7 +129,7 @@ public interface ConsumeEffect {
*
* @return sound effect
*/
Key sound();
Sound sound();
}
/**
@@ -2,8 +2,9 @@ package io.papermc.paper.dialog;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.InlinedRegistryBuilderProvider;
import io.papermc.paper.registry.data.RegistryBuilderProvider;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import java.util.function.Consumer;
import net.kyori.adventure.dialog.DialogLike;
@@ -19,7 +20,7 @@ import org.jetbrains.annotations.ApiStatus;
* Can also be created during bootstrap via {@link io.papermc.paper.registry.event.RegistryEvents#DIALOG}.
*/
@ApiStatus.NonExtendable
public interface Dialog extends Keyed, DialogLike {
public interface Dialog extends RegistryElement.Inlineable<Dialog, DialogRegistryEntry, DialogRegistryEntry.Builder>, Keyed, DialogLike {
/**
* Creates a new dialog using the provided builder.
@@ -28,7 +29,7 @@ public interface Dialog extends Keyed, DialogLike {
* @return a new dialog instance
*/
static Dialog create(final Consumer<RegistryBuilderFactory<Dialog, ? extends DialogRegistryEntry.Builder>> value) {
return InlinedRegistryBuilderProvider.instance().createDialog(value);
return RegistryBuilderProvider.instance().create(RegistryKey.DIALOG, value);
}
// Start generate - Dialog
@@ -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();
}
@@ -1,6 +1,7 @@
package io.papermc.paper.entity.poi;
import io.papermc.paper.InternalAPIBridge;
import io.papermc.paper.registry.RegistryElement;
import org.bukkit.Keyed;
import org.bukkit.block.data.BlockData;
import org.jspecify.annotations.NullMarked;
@@ -15,7 +16,7 @@ import org.jspecify.annotations.NullMarked;
* @see PoiTypes
*/
@NullMarked
public interface PoiType extends Keyed {
public interface PoiType extends RegistryElement<PoiType>, Keyed {
/**
* Determines whether the provided BlockState is relevant to this
@@ -0,0 +1,91 @@
package io.papermc.paper.event.entity;
import com.google.common.base.Preconditions;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Called when an {@link Entity} lands on the block after falling.
* This event is called before {@link org.bukkit.event.entity.EntityDamageEvent}, {@link org.bukkit.event.entity.EntityChangeBlockEvent}, {@link org.bukkit.event.player.PlayerInteractEvent}
* and {@link org.bukkit.event.entity.EntityInteractEvent}.
*/
@NullMarked
public class EntityLandEvent extends EntityEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final Block block;
private boolean cancelled;
private double fallDistance;
@ApiStatus.Internal
public EntityLandEvent(final Entity entity, final Block block, final double fallDistance) {
super(entity);
this.block = block;
this.fallDistance = fallDistance;
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
/**
* {@return the block that the entity landed on}
*/
public Block getBlock() {
return this.block;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
/**
* Sets whether to cancel the landing, cancelling this event will prevent
* the entity from taking fall damage and interact with the block. (ex: break turtle egg)
*
* @param cancel true if the event should be cancelled, false otherwise
*/
@Override
public void setCancelled(final boolean cancel) {
this.cancelled = cancel;
}
/**
* Gets the fall distance of the entity before landing.
*
* @return the fall distance
*/
public double getFallDistance() {
return this.fallDistance;
}
/**
* Sets the fall distance of the entity before landing.
* Fall distance in the event is used to calculate fall damage and
* the chance of transforming {@link org.bukkit.Material#FARMLAND} to {@link org.bukkit.Material#DIRT} and
* the type of falling sound when the entity lands on {@link org.bukkit.Material#POWDER_SNOW}.
*
* @param fallDistance the fall distance
* @throws IllegalArgumentException if the fall distance is negative or not finite
*/
public void setFallDistance(final @NonNegative double fallDistance) {
Preconditions.checkArgument(
Double.isFinite(fallDistance) && fallDistance >= 0.0,
"Fall distance must be finite and non-negative"
);
this.fallDistance = fallDistance;
}
}
@@ -0,0 +1,69 @@
package io.papermc.paper.event.entity;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Called when an entity is launched upward by a Potent Sulfur geyser.
*/
@NullMarked
public class EntityPotentSulfurLaunchEvent extends EntityEvent {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final Block sulfurBlock;
private final Location sourceLocation;
private final Vector launchVelocity;
@ApiStatus.Internal
public EntityPotentSulfurLaunchEvent(
final Entity entity,
final Block sulfurBlock,
final Location sourceLocation,
final Vector launchVelocity
) {
super(entity);
this.sulfurBlock = sulfurBlock;
this.sourceLocation = sourceLocation;
this.launchVelocity = launchVelocity;
}
/**
* Gets the Potent Sulfur block.
*/
public Block getSulfurBlock() {
return this.sulfurBlock;
}
/**
* Gets the location where the geyser exits the water column.
*/
public Location getSourceLocation() {
return this.sourceLocation.clone();
}
/**
* Gets the velocity that will be added to the entity.
* <br>
* This value is read-only, changes made to it <b>will not</b> have any
* effect on the final launch velocity.
*/
public Vector getLaunchVelocity() {
return this.launchVelocity.clone();
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}
@@ -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,8 @@
package io.papermc.paper.persistence;
import java.util.Set;
import net.kyori.adventure.key.Key;
import com.google.common.base.Preconditions;
import org.bukkit.NamespacedKey;
import org.bukkit.persistence.PersistentDataAdapterContext;
import org.bukkit.persistence.PersistentDataContainer;
@@ -66,6 +68,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 +138,63 @@ 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);
}
/**
* Estimates the {@link PersistentDataType} of the value based on the primitive
* type that is stored for the given key.
* <br>
* <b>WARNING:</b> This method will only return data types
* defined in {@link PersistentDataType} which directly map to a specific
* nbt tag. The return value will be wrong if the data type is plugin
* specific or not represented by a specific nbt tag.
* Use {@link #has(NamespacedKey, PersistentDataType)} if you expect a
* specific data type.
*
* @param key the key to look up the value type of
* @return estimated data type or null if no value is present, the type is
* unknown or an empty list is encountered as value.
*/
@Nullable PersistentDataType<?, ?> getPrimitiveStorageType(Key key);
/**
* 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,70 @@
package io.papermc.paper.registry;
import io.papermc.paper.registry.tag.TagKey;
import net.kyori.adventure.key.Key;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* An element tied to a registry.
* <p>
* For unregistered element, the equality methods exposed here will always return {@code false}
* as there's no way to identify them with a key.
*
* @see RegistryKey
* @see org.bukkit.Registry
* @see org.bukkit.Registry#getKey(org.bukkit.Keyed)
*/
@NullMarked
@ApiStatus.Experimental
public interface RegistryElement<T> {
/**
* Checks whether this element is identified by the given key.
*
* @param type the key
* @return whether this element is identified by the given key.
*/
boolean is(TypedKey<T> type);
/**
* Checks whether this element is identified by the given key.
*
* @param type the key
* @return whether this element is identified by the given key
*/
boolean is(Key type);
/**
* Checks whether this element is contained in the
* tag identified by the given tag key.
*
* @param type the tag key
* @return whether this element is contained in the tag
*/
boolean is(TagKey<T> type);
/**
* Marker interface for registered types that link an API type to its
* registry entry and builder types.
*
* @param <T> the API type
* @param <E> the registry entry type
* @param <B> the builder type
*/
@SuppressWarnings("unused")
@ApiStatus.Experimental
interface Buildable<T, E, B extends RegistryBuilder<T>> extends RegistryElement<T> {
}
/**
* Marker interface for registered types that are valid to be constructed inline.
*
* @param <T> the API type
* @param <E> the registry entry type
* @param <B> the builder type
*/
@ApiStatus.Experimental
interface Inlineable<T, E, B extends RegistryBuilder<T>> extends RegistryElement.Buildable<T, E, B> {
}
}
@@ -2,8 +2,6 @@ package io.papermc.paper.registry;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import net.kyori.adventure.key.Keyed;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
@@ -12,14 +10,13 @@ import org.jspecify.annotations.NullMarked;
* @param <T> the value type for the registry
*/
@NullMarked
public sealed interface TypedKey<T> extends Key permits TypedKeyImpl {
public sealed interface TypedKey<T> permits TypedKeyImpl {
/**
* Gets the key for the value in the registry.
*
* @return the value's key
*/
@Override
Key key();
/**
@@ -1,26 +1,8 @@
package io.papermc.paper.registry;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.jspecify.annotations.NullMarked;
@NullMarked
record TypedKeyImpl<T>(Key key, RegistryKey<T> registryKey) implements TypedKey<T> {
// Wrap key methods to make this easier to use
@KeyPattern.Namespace
@Override
public String namespace() {
return this.key.namespace();
}
@KeyPattern.Value
@Override
public String value() {
return this.key.value();
}
@Override
public String asString() {
return this.key.asString();
}
}
@@ -161,6 +161,7 @@ public interface EnchantmentRegistryEntry {
* @param supportedItems the registry key set representing the supported items
* @return this builder instance
* @see RegistrySet#keySet(RegistryKey, TypedKey[])
* @see RegistrySet#keySet(TypedKey, TypedKey[])
* @see RegistryComposeEvent#getOrCreateTag(TagKey)
*/
@Contract(value = "_ -> this", mutates = "this")
@@ -182,6 +183,7 @@ public interface EnchantmentRegistryEntry {
* @param primaryItems the registry key set representing the primary items.
* @return this builder instance.
* @see RegistrySet#keySet(RegistryKey, TypedKey[])
* @see RegistrySet#keySet(TypedKey, TypedKey[])
* @see RegistryComposeEvent#getOrCreateTag(TagKey)
*/
@Contract(value = "_ -> this", mutates = "this")
@@ -285,6 +287,7 @@ public interface EnchantmentRegistryEntry {
* @param exclusiveWith a registry set of enchantments exclusive to this one
* @return this builder instance
* @see RegistrySet#keySet(RegistryKey, TypedKey[])
* @see RegistrySet#keySet(TypedKey, TypedKey[])
* @see RegistryComposeEvent#getOrCreateTag(TagKey)
*/
@Contract(value = "_ -> this", mutates = "this")
@@ -1,35 +0,0 @@
package io.papermc.paper.registry.data;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.function.Consumer;
import org.bukkit.MusicInstrument;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
import org.jetbrains.annotations.ApiStatus;
/**
* @hidden
*/
@ApiStatus.Internal
@ApiStatus.NonExtendable
public interface InlinedRegistryBuilderProvider {
static InlinedRegistryBuilderProvider instance() {
final class Holder {
static final Optional<InlinedRegistryBuilderProvider> INSTANCE = ServiceLoader.load(InlinedRegistryBuilderProvider.class, InlinedRegistryBuilderProvider.class.getClassLoader()).findFirst();
}
return Holder.INSTANCE.orElseThrow();
}
MusicInstrument createInstrument(Consumer<RegistryBuilderFactory<MusicInstrument, ? extends InstrumentRegistryEntry.Builder>> value);
Dialog createDialog(Consumer<RegistryBuilderFactory<Dialog, ? extends DialogRegistryEntry.Builder>> value);
TrimMaterial createTrimMaterial(Consumer<RegistryBuilderFactory<TrimMaterial, ? extends TrimMaterialRegistryEntry.Builder>> value);
TrimPattern createTrimPattern(Consumer<RegistryBuilderFactory<TrimPattern, ? extends TrimPatternRegistryEntry.Builder>> value);
}
@@ -0,0 +1,36 @@
package io.papermc.paper.registry.data;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.holder.RegistryHolder;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.function.Consumer;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
/**
* @hidden
*/
@ApiStatus.Internal
@ApiStatus.NonExtendable
public interface RegistryBuilderProvider {
static RegistryBuilderProvider instance() {
final class Holder {
static final Optional<RegistryBuilderProvider> INSTANCE = ServiceLoader.load(RegistryBuilderProvider.class, RegistryBuilderProvider.class.getClassLoader()).findFirst();
}
return Holder.INSTANCE.orElseThrow();
}
static <T extends RegistryElement.Buildable<T, E, ?>, E> RegistryHolder<T, E> transientHolder(final TypedKey<T> key) {
return instance().createTransientReferenceHolder(key);
}
<T extends RegistryElement.Inlineable<T, ?, B> & Keyed, B extends RegistryBuilder<T>> T create(RegistryKey<T> key, Consumer<RegistryBuilderFactory<T, ? extends B>> value);
<T extends RegistryElement.Buildable<T, E, ?>, E> RegistryHolder.Reference<T, E> createTransientReferenceHolder(TypedKey<T> key);
}
@@ -2,6 +2,7 @@ package io.papermc.paper.registry.data;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.holder.RegistryHolder;
import io.papermc.paper.registry.set.RegistryKeySet;
import java.util.List;
import org.bukkit.Sound;
@@ -16,6 +17,7 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.Nullable;
import static io.papermc.paper.registry.data.RegistryBuilderProvider.transientHolder;
import static io.papermc.paper.util.BoundChecker.requireNonNegative;
import static io.papermc.paper.util.BoundChecker.requirePositive;
@@ -180,18 +182,15 @@ public interface SulfurCubeArchetypeRegistryEntry {
@ApiStatus.NonExtendable
interface SoundSettings {
// todo The sounds should take a RegistryHolder but need more thoughts on the create method
/**
* {@return the sound played once the sulfur cube is knocked}
*/
@ApiStatus.Experimental
TypedKey<Sound> hitSound();
RegistryHolder<Sound, SoundEventRegistryEntry> hitSound();
/**
* {@return the sound played once the sulfur cube is pushed}
*/
@ApiStatus.Experimental
TypedKey<Sound> pushSound();
RegistryHolder<Sound, SoundEventRegistryEntry> pushSound();
/**
* {@return the smallest impulse required to play the push sound}
@@ -199,7 +198,7 @@ public interface SulfurCubeArchetypeRegistryEntry {
float pushSoundImpulseThreshold();
/**
* {@return the amount of seconds before the push sound can be played again}
* {@return the number of seconds before the push sound can be played again}
*/
float pushSoundCooldown();
@@ -209,13 +208,28 @@ public interface SulfurCubeArchetypeRegistryEntry {
* @param hitSound the sound played once the sulfur cube is knocked, as returned by {@link #hitSound()}
* @param pushSound the sound played once the sulfur cube is pushed, as returned by {@link #pushSound()}
* @param pushSoundImpulseThreshold the smallest impulse required to play the push sound, as returned by {@link #pushSoundImpulseThreshold()}
* @param pushSoundCooldown the amount of seconds before the push sound can be played again, as returned by {@link #pushSoundCooldown()}
* @param pushSoundCooldown the number of seconds before the push sound can be played again, as returned by {@link #pushSoundCooldown()}
* @return the created instance
*/
@Contract(value = "_, _, _, _ -> new", pure = true)
@ApiStatus.Experimental
static SoundSettings of(final TypedKey<Sound> hitSound, final TypedKey<Sound> pushSound, final float pushSoundImpulseThreshold, final float pushSoundCooldown) {
record Impl(TypedKey<Sound> hitSound, TypedKey<Sound> pushSound, float pushSoundImpulseThreshold, float pushSoundCooldown) implements SoundSettings {
return of(transientHolder(hitSound), transientHolder(pushSound), pushSoundImpulseThreshold, pushSoundCooldown);
}
/**
* Creates a new sound settings instance based on the passed values.
* <p>
* {@link RegistryHolder} instances can be created with {@link io.papermc.paper.registry.event.RegistryFactory}.
*
* @param hitSound the sound played once the sulfur cube is knocked, as returned by {@link #hitSound()}
* @param pushSound the sound played once the sulfur cube is pushed, as returned by {@link #pushSound()}
* @param pushSoundImpulseThreshold the smallest impulse required to play the push sound, as returned by {@link #pushSoundImpulseThreshold()}
* @param pushSoundCooldown the number of seconds before the push sound can be played again, as returned by {@link #pushSoundCooldown()}
* @return the created instance
*/
@Contract(value = "_, _, _, _ -> new", pure = true)
static SoundSettings of(final RegistryHolder<Sound, SoundEventRegistryEntry> hitSound, final RegistryHolder<Sound, SoundEventRegistryEntry> pushSound, final float pushSoundImpulseThreshold, final float pushSoundCooldown) {
record Impl(RegistryHolder<Sound, SoundEventRegistryEntry> hitSound, RegistryHolder<Sound, SoundEventRegistryEntry> pushSound, float pushSoundImpulseThreshold, float pushSoundCooldown) implements SoundSettings {
}
return new Impl(hitSound, pushSound, pushSoundImpulseThreshold, pushSoundCooldown);
@@ -48,8 +48,10 @@ public interface DialogRegistryEntry {
* <p>Not a part of the registry entry.</p>
*
* @return a new registry value set builder
* @deprecated use {@link io.papermc.paper.registry.event.RegistryFactory} on the registry event and {@link io.papermc.paper.registry.set.RegistryHolderSetBuilder}
*/
@Contract(value = "-> new", pure = true)
@Deprecated(since = "26.4", forRemoval = true)
RegistryValueSetBuilder<Dialog, DialogRegistryEntry.Builder> registryValueSet();
/**
@@ -114,7 +114,7 @@ public non-sealed interface TextDialogInput extends DialogInput {
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder width(final @Range(from = 1, to = 1024) int width);
Builder width(@Range(from = 1, to = 1024) int width);
/**
* Sets whether the label should be visible.
@@ -123,7 +123,7 @@ public non-sealed interface TextDialogInput extends DialogInput {
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder labelVisible(final boolean labelVisible);
Builder labelVisible(boolean labelVisible);
/**
* Sets the initial value of the input.
@@ -132,7 +132,7 @@ public non-sealed interface TextDialogInput extends DialogInput {
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder initial(final String initial);
Builder initial(String initial);
/**
* Sets the maximum length of the input.
@@ -141,7 +141,7 @@ public non-sealed interface TextDialogInput extends DialogInput {
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder maxLength(final @Positive int maxLength);
Builder maxLength(@Positive int maxLength);
/**
* Sets the multiline options for the input.
@@ -150,7 +150,7 @@ public non-sealed interface TextDialogInput extends DialogInput {
* @return this builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder multiline(final @Nullable MultilineOptions multiline);
Builder multiline(@Nullable MultilineOptions multiline);
/**
* Builds the text dialog input.
@@ -61,7 +61,7 @@ public non-sealed interface DialogListType extends DialogType {
* @return the builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder exitAction(final @Nullable ActionButton exitAction);
Builder exitAction(@Nullable ActionButton exitAction);
/**
* Sets the number of columns to display in the dialog list.
@@ -70,7 +70,7 @@ public non-sealed interface DialogListType extends DialogType {
* @return the builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder columns(final @Positive int columns);
Builder columns(@Positive int columns);
/**
* Sets the width of each button in the dialog list.
@@ -79,7 +79,7 @@ public non-sealed interface DialogListType extends DialogType {
* @return the builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder buttonWidth(final @Range(from = 1, to = 1024) int buttonWidth);
Builder buttonWidth(@Range(from = 1, to = 1024) int buttonWidth);
/**
* Builds the dialog list type.
@@ -53,7 +53,7 @@ public non-sealed interface MultiActionType extends DialogType {
* @return the builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder exitAction(final @Nullable ActionButton exitAction);
Builder exitAction(@Nullable ActionButton exitAction);
/**
* Sets the number of columns to display in the dialog.
@@ -62,7 +62,7 @@ public non-sealed interface MultiActionType extends DialogType {
* @return the builder
*/
@Contract(value = "_ -> this", mutates = "this")
Builder columns(final @Positive int columns);
Builder columns(@Positive int columns);
/**
* Builds the multi-action dialog.
@@ -7,7 +7,7 @@ import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
/**
* Event object for {@link RegistryEventProvider#compose()}. This
* Event object for {@link RegistryEvents#compose(io.papermc.paper.registry.RegistryKey)}. This
* event is fired after a registry is loaded with its normal values.
* It provides a way for plugins to add new objects to the registry.
*
@@ -34,4 +34,11 @@ public interface RegistryComposeEvent<T, B extends RegistryBuilder<T>> extends R
* @param <V> the tag value type
*/
<V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey); // TODO remove Keyed
/**
* Gets the factory for creating tags, holders, and holder sets.
*
* @return the registry factory
*/
RegistryFactory factory();
}
@@ -8,8 +8,8 @@ import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
/**
* Event object for {@link RegistryEventProvider#entryAdd()}. This
* event is fired right before a specific entry is registered in/added to registry.
* Event object for {@link RegistryEvents#entryAdd(io.papermc.paper.registry.RegistryKey)}. This
* event is fired right before a specific entry is registered in/added to the registry.
* It provides a way for plugins to modify parts of this entry.
*
* @param <T> registry entry type
@@ -42,4 +42,11 @@ public interface RegistryEntryAddEvent<T, B extends RegistryBuilder<T>> extends
* @param <V> the tag value type
*/
<V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey);
/**
* Gets the factory for creating tags, holders, and holder sets.
*
* @return the registry factory
*/
RegistryFactory factory();
}
@@ -4,6 +4,7 @@ import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
import org.jetbrains.annotations.ApiStatus;
@@ -19,9 +20,11 @@ import org.jetbrains.annotations.ApiStatus;
*
* @param <T> registry entry type
* @param <B> registry entry builder type
* @deprecated use {@link RegistryEvents#compose(RegistryKey)} and {@link RegistryEvents#entryAdd(RegistryKey)}
*/
@ApiStatus.NonExtendable
public interface RegistryEventProvider<T, B extends RegistryBuilder<T>> {
@Deprecated(forRemoval = true, since = "26.4")
public interface RegistryEventProvider<T extends RegistryElement.Buildable<T, ?, B>, B extends RegistryBuilder<T>> {
/**
* Gets the event type for {@link RegistryEntryAddEvent} which is fired just before
@@ -3,24 +3,25 @@ package io.papermc.paper.registry.event;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
record RegistryEventProviderImpl<T, B extends RegistryBuilder<T>>(RegistryKey<T> registryKey) implements RegistryEventProvider<T, B> {
record RegistryEventProviderImpl<T extends RegistryElement.Buildable<T, E, B>, E, B extends RegistryBuilder<T>>(RegistryKey<T> registryKey) implements RegistryEventProvider<T, B> {
static <T, B extends RegistryBuilder<T>> RegistryEventProvider<T, B> create(final RegistryKey<T> registryKey) {
static <T extends RegistryElement.Buildable<T, E, B>, E, B extends RegistryBuilder<T>> RegistryEventProvider<T, B> create(final RegistryKey<T> registryKey) {
return new RegistryEventProviderImpl<>(registryKey);
}
@Override
public RegistryEntryAddEventType<T, B> entryAdd() {
return RegistryEventTypeProvider.provider().registryEntryAdd(this);
return RegistryEvents.entryAdd(this.registryKey);
}
@Override
public LifecycleEventType.Prioritizable<BootstrapContext, RegistryComposeEvent<T, B>> compose() {
return RegistryEventTypeProvider.provider().registryCompose(this);
return RegistryEvents.compose(this.registryKey);
}
}
@@ -3,6 +3,8 @@ package io.papermc.paper.registry.event;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
import java.util.Optional;
import java.util.ServiceLoader;
@@ -17,7 +19,7 @@ interface RegistryEventTypeProvider {
return PROVIDER.orElseThrow(() -> new IllegalStateException("Could not find a %s service implementation".formatted(RegistryEventTypeProvider.class.getSimpleName())));
}
<T, B extends RegistryBuilder<T>> RegistryEntryAddEventType<T, B> registryEntryAdd(RegistryEventProvider<T, B> type);
<API extends RegistryElement.Buildable<API, E, B>, E, B extends RegistryBuilder<API>> RegistryEntryAddEventType<API, B> registryEntryAdd(RegistryKey<API> key);
<T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryComposeEvent<T, B>> registryCompose(RegistryEventProvider<T, B> type);
<API extends RegistryElement.Buildable<API, E, B>, E, B extends RegistryBuilder<API>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryComposeEvent<API, B>> registryCompose(RegistryKey<API> key);
}
@@ -2,6 +2,10 @@ package io.papermc.paper.registry.event;
import io.papermc.paper.block.pot.PotPatternType;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.BannerPatternRegistryEntry;
import io.papermc.paper.registry.data.CatTypeRegistryEntry;
@@ -22,6 +26,7 @@ import io.papermc.paper.registry.data.TrimPatternRegistryEntry;
import io.papermc.paper.registry.data.WolfVariantRegistryEntry;
import io.papermc.paper.registry.data.ZombieNautilusVariantRegistryEntry;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
import org.bukkit.Art;
import org.bukkit.GameEvent;
import org.bukkit.JukeboxSong;
@@ -43,32 +48,141 @@ import org.bukkit.inventory.meta.trim.TrimPattern;
import static io.papermc.paper.registry.event.RegistryEventProviderImpl.create;
/**
* Holds providers for {@link RegistryEntryAddEvent} and {@link RegistryComposeEvent}
* handlers for each applicable registry.
* Holds methods to create registry event types for various registry-related events.
*/
public final class RegistryEvents {
// Start generate - RegistryEvents
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<GameEvent, GameEventRegistryEntry.Builder> GAME_EVENT = create(RegistryKey.GAME_EVENT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<TrimMaterial, TrimMaterialRegistryEntry.Builder> TRIM_MATERIAL = create(RegistryKey.TRIM_MATERIAL);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<TrimPattern, TrimPatternRegistryEntry.Builder> TRIM_PATTERN = create(RegistryKey.TRIM_PATTERN);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<DamageType, DamageTypeRegistryEntry.Builder> DAMAGE_TYPE = create(RegistryKey.DAMAGE_TYPE);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<Wolf.Variant, WolfVariantRegistryEntry.Builder> WOLF_VARIANT = create(RegistryKey.WOLF_VARIANT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<Enchantment, EnchantmentRegistryEntry.Builder> ENCHANTMENT = create(RegistryKey.ENCHANTMENT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<JukeboxSong, JukeboxSongRegistryEntry.Builder> JUKEBOX_SONG = create(RegistryKey.JUKEBOX_SONG);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<PatternType, BannerPatternRegistryEntry.Builder> BANNER_PATTERN = create(RegistryKey.BANNER_PATTERN);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<PotPatternType, DecoratedPotPatternRegistryEntry.Builder> DECORATED_POT_PATTERN = create(RegistryKey.DECORATED_POT_PATTERN);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<Art, PaintingVariantRegistryEntry.Builder> PAINTING_VARIANT = create(RegistryKey.PAINTING_VARIANT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<MusicInstrument, InstrumentRegistryEntry.Builder> INSTRUMENT = create(RegistryKey.INSTRUMENT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<Cat.Type, CatTypeRegistryEntry.Builder> CAT_VARIANT = create(RegistryKey.CAT_VARIANT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<Frog.Variant, FrogVariantRegistryEntry.Builder> FROG_VARIANT = create(RegistryKey.FROG_VARIANT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<Chicken.Variant, ChickenVariantRegistryEntry.Builder> CHICKEN_VARIANT = create(RegistryKey.CHICKEN_VARIANT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<Cow.Variant, CowVariantRegistryEntry.Builder> COW_VARIANT = create(RegistryKey.COW_VARIANT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<Pig.Variant, PigVariantRegistryEntry.Builder> PIG_VARIANT = create(RegistryKey.PIG_VARIANT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<ZombieNautilus.Variant, ZombieNautilusVariantRegistryEntry.Builder> ZOMBIE_NAUTILUS_VARIANT = create(RegistryKey.ZOMBIE_NAUTILUS_VARIANT);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<SulfurCube.Archetype, SulfurCubeArchetypeRegistryEntry.Builder> SULFUR_CUBE_ARCHETYPE = create(RegistryKey.SULFUR_CUBE_ARCHETYPE);
/**
* @deprecated use {@link #entryAdd(RegistryKey)} and {@link #compose(RegistryKey)}
*/
@Deprecated(forRemoval = true, since = "26.4")
public static final RegistryEventProvider<Dialog, DialogRegistryEntry.Builder> DIALOG = create(RegistryKey.DIALOG);
// End generate - RegistryEvents
/**
* Gets the event type for {@link RegistryEntryAddEvent} which is fired just before
* an object is added to a registry.
* <p>
* Can be used in {@link io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager#registerEventHandler(LifecycleEventType, io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler)}
* to register a handler for {@link RegistryEntryAddEvent}.
*
* @param registryKey the registry key
* @param <API> the api type
* @param <E> the registry entry type
* @param <B> the registry entry builder type
*
* @return the registry entry add event type
*/
public static <API extends RegistryElement.Buildable<API, E, B>, E, B extends RegistryBuilder<API>> RegistryEntryAddEventType<API, B> entryAdd(final RegistryKey<API> registryKey) {
return RegistryEventTypeProvider.provider().registryEntryAdd(registryKey);
}
/**
* Gets the event type for {@link RegistryComposeEvent} which is fired after
* a registry is loaded of expected elements. It allows for the registration of new objects.
* <p>
* Can be used in {@link io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager#registerEventHandler(LifecycleEventType, io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler)}
* to register a handler for {@link RegistryComposeEvent}.
*
* @param registryKey the registry key
* @param <API> the api type
* @param <E> the registry entry type
* @param <B> the registry entry builder type
*
* @return the registry compose event type
*/
public static <API extends RegistryElement.Buildable<API, E, B>, E, B extends RegistryBuilder<API>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryComposeEvent<API, B>> compose(final RegistryKey<API> registryKey) {
return RegistryEventTypeProvider.provider().registryCompose(registryKey);
}
private RegistryEvents() {
}
@@ -0,0 +1,76 @@
package io.papermc.paper.registry.event;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.holder.RegistryHolder;
import io.papermc.paper.registry.set.RegistryHolderSetBuilder;
import io.papermc.paper.registry.tag.Tag;
import io.papermc.paper.registry.tag.TagKey;
import java.util.function.Consumer;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
/**
* A factory for creating tags, holders, and holder sets while
* reacting to registry events.
*/
@ApiStatus.Experimental
public interface RegistryFactory {
/**
* Gets or creates a tag for the given tag key. This tag
* is then required to be filled either from the built-in or
* custom datapack.
*
* @param tagKey the tag key
* @return the tag
* @param <V> the tag value type
*/
<V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey); // TODO remove Keyed
/**
* Gets or creates a reference holder for the given key. The referenced
* value does not need to be present in the registry yet.
* <p>
* You can only create new reference holders for registries in the same registry layer. For example,
* since {@link RegistryKey#SOUND_EVENT} is a built-in registry, you cannot create new
* reference holders while in an event for {@link RegistryKey#SULFUR_CUBE_ARCHETYPE} as that
* is in a later layer. Try a {@link #createInlinedHolder(RegistryKey, Consumer)}.
*
* @param key the key of the referenced value
* @return the reference holder
* @param <V> the API type
* @param <E> the registry entry type
* @param <B> the builder type
* @see #createInlinedHolder(RegistryKey, Consumer)
*/
<V extends Keyed & RegistryElement.Buildable<V, E, B>, E, B extends RegistryBuilder<V>> RegistryHolder.Reference<V, E> getOrCreateReferenceHolder(TypedKey<V> key); // TODO remove Keyed
/**
* Creates an inlined holder wrapping an anonymous value built from the
* given builder consumer.
*
* @param registryKey the registry key for the value's type
* @param value a consumer configuring the inlined value's builder
* @return the inlined holder
* @param <V> the API type
* @param <E> the registry entry type
* @param <B> the builder type
* @see #getOrCreateReferenceHolder(TypedKey)
*/
<V extends Keyed & RegistryElement.Inlineable<V, E, B>, E, B extends RegistryBuilder<V>> RegistryHolder.Inlined<V, E> createInlinedHolder(RegistryKey<V> registryKey, Consumer<RegistryBuilderFactory<V, ? extends B>> value); // TODO remove Keyed
/**
* Creates a new builder for a {@link RegistryHolderSetBuilder holder set} of the given registry.
*
* @param registryKey the registry key for the value's type
* @return a new holder set builder
* @param <V> the API type
* @param <E> the registry entry type
* @param <B> the builder type
*/
<V extends Keyed & RegistryElement.Inlineable<V, E, B>, E, B extends RegistryBuilder<V>> RegistryHolderSetBuilder<V, E, B> createSetBuilder(RegistryKey<V> registryKey); // TODO remove Keyed
}
@@ -1,5 +1,7 @@
package io.papermc.paper.registry.holder;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import org.jetbrains.annotations.ApiStatus;
@@ -12,7 +14,28 @@ import org.jetbrains.annotations.ApiStatus;
* @param <ENTRY> the type of the registry entry (for inlined values)
*/
@ApiStatus.Experimental
public sealed interface RegistryHolder<API, ENTRY> permits RegistryHolder.Reference, RegistryHolder.Inlined {
public sealed interface RegistryHolder<API extends RegistryElement.Buildable<API, ENTRY, ?>, ENTRY> permits RegistryHolder.Reference, RegistryHolder.Inlined {
/**
* Gets the registry key of the registry this holder's value belongs to.
*
* @return the registry key
*/
RegistryKey<API> registryKey();
/**
* Gets the value held by this holder.
*
* @return the value
*/
API value();
/**
* Gets the registry entry held by this holder.
*
* @return the entry
*/
ENTRY entry();
/**
* A holder that references a registry value by key, but does not have the entry itself.
@@ -22,7 +45,7 @@ public sealed interface RegistryHolder<API, ENTRY> permits RegistryHolder.Refere
* @param <ENTRY> the type of the registry entry
*/
@ApiStatus.NonExtendable
non-sealed interface Reference<API, ENTRY> extends RegistryHolder<API, ENTRY> {
non-sealed interface Reference<API extends RegistryElement.Buildable<API, ENTRY, ?>, ENTRY> extends RegistryHolder<API, ENTRY> {
/**
* The key of the referenced value.
@@ -39,13 +62,14 @@ public sealed interface RegistryHolder<API, ENTRY> permits RegistryHolder.Refere
* @param <ENTRY> the type of the registry entry
*/
@ApiStatus.NonExtendable
non-sealed interface Inlined<API, ENTRY> extends RegistryHolder<API, ENTRY> {
non-sealed interface Inlined<API extends RegistryElement.Buildable<API, ENTRY, ?>, ENTRY> extends RegistryHolder<API, ENTRY> {
/**
* The inlined entry.
*
* @return the inlined entry
*/
@Override
ENTRY entry();
}
}
@@ -0,0 +1,28 @@
package io.papermc.paper.registry.set;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.holder.RegistryHolder;
import java.util.Collection;
import org.jetbrains.annotations.Unmodifiable;
/**
* A {@link RegistrySet} backed by {@link RegistryHolder holders}, each of which is
* either a reference to a registry value or an inlined, anonymous value.
* <p>
* Create with {@link RegistrySet#holderSetBuilder(RegistryKey)}, or if in a registry event context,
* {@link io.papermc.paper.registry.event.RegistryFactory#createSetBuilder(RegistryKey)}.
* </p>
*
* @param <T> the API type
* @param <E> the registry entry type
*/
public non-sealed interface RegistryHolderSet<T extends RegistryElement.Buildable<T, E, ?>, E> extends RegistrySet<T> {
/**
* Gets the holders in this set.
*
* @return an unmodifiable collection of holders
*/
@Unmodifiable Collection<RegistryHolder<T, E>> holders();
}
@@ -0,0 +1,49 @@
package io.papermc.paper.registry.set;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.TypedKey;
import java.util.function.Consumer;
/**
* A builder for a {@link RegistryHolderSet}.
*
* @param <T> the API type
* @param <E> the registry entry type
* @param <B> the builder type
*/
public interface RegistryHolderSetBuilder<T extends RegistryElement.Inlineable<T, E, B>, E, B extends RegistryBuilder<T>> {
/**
* Adds a value to the set.
*
* @param value the value to add
* @return this builder
*/
RegistryHolderSetBuilder<T, E, B> add(T value);
/**
* Adds a value by its key to the set. The referenced value does not need
* to be present in the registry yet.
*
* @param key the key of the value to add
* @return this builder
*/
RegistryHolderSetBuilder<T, E, B> add(TypedKey<T> key);
/**
* Adds an inlined, anonymous value built from the given builder consumer to the set.
*
* @param value a consumer configuring the inlined value's builder
* @return this builder
*/
RegistryHolderSetBuilder<T, E, B> add(Consumer<RegistryBuilderFactory<T, ? extends B>> value);
/**
* Builds the holder set.
*
* @return a new holder set
*/
RegistryHolderSet<T, E> build();
}
@@ -3,7 +3,6 @@ package io.papermc.paper.registry.set;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.tag.Tag;
import io.papermc.paper.registry.tag.TagKey;
import java.util.Collection;
import java.util.Iterator;
import org.bukkit.Keyed;
@@ -43,12 +42,24 @@ public non-sealed interface RegistryKeySet<T extends Keyed> extends Iterable<Typ
* Resolve this set into a collection of values. Prefer using
* {@link #values()}.
*
* @param registry the registry to resolve the values from (must match {@link #registryKey()})
* @param ignored the registry to resolve the values from (must match {@link #registryKey()})
* @return the resolved values
* @see RegistryKeySet#values()
* @see Registry#getTagValues(TagKey)
* @deprecated use {@link #resolve()}, the registry is not needed
*/
@Unmodifiable Collection<T> resolve(final Registry<T> registry);
@Deprecated(since = "26.4", forRemoval = true)
default @Unmodifiable Collection<T> resolve(final Registry<T> ignored) {
return this.resolve();
}
/**
* Resolve this set into a collection of values. Prefer using
* {@link #values()}.
*
* @return the resolved values
* @see RegistryKeySet#values()
*/
@Unmodifiable Collection<T> resolve();
/**
* Checks if this set contains the value with the given key.
@@ -37,7 +37,8 @@ record RegistryKeySetImpl<T extends Keyed>(RegistryKey<T> registryKey, List<Type
}
@Override
public Collection<T> resolve(final Registry<T> registry) {
public Collection<T> resolve() {
final Registry<T> registry = RegistryAccess.registryAccess().getRegistry(this.registryKey);
final List<T> values = new ArrayList<>(this.values.size());
for (final TypedKey<T> key : this.values) {
final T value = registry.get(key.key());
@@ -1,9 +1,12 @@
package io.papermc.paper.registry.set;
import com.google.common.collect.Lists;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.tag.Tag;
import java.util.List;
import org.bukkit.Keyed;
import org.jetbrains.annotations.Contract;
@@ -16,13 +19,30 @@ import org.jetbrains.annotations.Contract;
* These are obtained via {@link org.bukkit.Registry#getTag(io.papermc.paper.registry.tag.TagKey)}.</li>
* <li>{@link RegistryKeySet} which is a set of keys linked to values that are present in the registry. These are
* created via {@link #keySet(RegistryKey, Iterable)} or {@link #keySetFromValues(RegistryKey, Iterable)}.</li>
* <li>{@link RegistryValueSet} which is a set of values which are anonymous (don't have keys in the registry). These are
* created via {@link #valueSet(RegistryKey, Iterable)}.</li>
* <li>{@link RegistryHolderSet} which is a mixed set of values, some of which are anonymous (don't have keys in the registry). These are
* created via {@link #holderSetBuilder(RegistryKey)}.</li>
* </ul>
*
* @param <T> registry value type
*/
public sealed interface RegistrySet<T> permits RegistryKeySet, RegistryValueSet {
public sealed interface RegistrySet<T> permits RegistryHolderSet, RegistryKeySet, RegistryValueSet {
/**
* Creates a new builder for a {@link RegistryHolderSet}, a mixed set of registry-backed
* references and inlined, anonymous values.
* <p>
* In a registry event context, use {@link io.papermc.paper.registry.event.RegistryFactory#createSetBuilder(RegistryKey)} instead.
*
* @param registryKey the registry key for the type of these values
* @return a new holder set builder
* @param <T> the API type
* @param <E> the registry entry type
* @param <B> the builder type
*/
@Contract(value = "_ -> new", pure = true)
static <T extends Keyed & RegistryElement.Inlineable<T, E, B>, E, B extends RegistryBuilder<T>> RegistryHolderSetBuilder<T, E, B> holderSetBuilder(final RegistryKey<T> registryKey) { // TODO remove Keyed
return RegistrySetProvider.instance().registryHolderSetBuilder(registryKey);
}
/**
* Creates a {@link RegistryValueSet} from anonymous values.
@@ -32,8 +52,10 @@ public sealed interface RegistrySet<T> permits RegistryKeySet, RegistryValueSet
* @param values the values
* @return a new registry set
* @param <T> the type of the values
* @deprecated Use {@link #holderSetBuilder(RegistryKey)}
*/
@Contract(value = "_, _ -> new", pure = true)
@Deprecated(since = "26.3", forRemoval = true)
static <T> RegistryValueSet<T> valueSet(final RegistryKey<T> registryKey, final Iterable<? extends T> values) {
return RegistryValueSetImpl.create(registryKey, values);
}
@@ -41,7 +63,7 @@ public sealed interface RegistrySet<T> permits RegistryKeySet, RegistryValueSet
/**
* Creates a {@link RegistryKeySet} from registry-backed values.
* <p>All values provided <b>must</b> have keys in the given registry.
* <!--For anonymous values, use {@link #valueSet(RegistryKey, Iterable)}--></p>
* For anonymous values, use {@link #valueSet(RegistryKey, Iterable)}.</p>
* <p>If references to actual objects are not available yet, use {@link #keySet(RegistryKey, Iterable)} to
* create an equivalent {@link RegistryKeySet} using just {@link TypedKey TypedKeys}.</p>
*
@@ -57,7 +79,7 @@ public sealed interface RegistrySet<T> permits RegistryKeySet, RegistryValueSet
}
/**
* Creates a direct {@link RegistrySet} from {@link TypedKey TypedKeys}.
* Creates a direct {@code RegistrySet} from {@link TypedKey TypedKeys}.
*
* @param registryKey the registry key for the owner of these keys
* @param keys the keys for the values
@@ -70,7 +92,22 @@ public sealed interface RegistrySet<T> permits RegistryKeySet, RegistryValueSet
}
/**
* Creates a direct {@link RegistrySet} from {@link TypedKey TypedKeys}.
* Creates a non empty direct {@code RegistrySet} from {@link TypedKey TypedKeys}.
*
* @param firstKey the first key for the values
* @param otherKeys the other keys for the values
* @return a new registry set
* @param <T> the type of the values
*/
@SafeVarargs
static <T extends Keyed> RegistryKeySet<T> keySet(final TypedKey<T> firstKey, final TypedKey<T>... otherKeys) { // TODO remove Keyed
final List<TypedKey<T>> keys = Lists.newArrayList(otherKeys);
keys.addFirst(firstKey);
return keySet(firstKey.registryKey(), keys);
}
/**
* Creates a direct {@code RegistrySet} from {@link TypedKey TypedKeys}.
*
* @param registryKey the registry key for the owner of these keys
* @param keys the keys for the values
@@ -0,0 +1,26 @@
package io.papermc.paper.registry.set;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import java.util.Optional;
import java.util.ServiceLoader;
import org.bukkit.Keyed;
import org.jetbrains.annotations.ApiStatus;
/**
* @hidden
*/
@ApiStatus.Internal
@ApiStatus.NonExtendable
public interface RegistrySetProvider {
static RegistrySetProvider instance() {
final class Holder {
static final Optional<RegistrySetProvider> INSTANCE = ServiceLoader.load(RegistrySetProvider.class, RegistrySetProvider.class.getClassLoader()).findFirst();
}
return Holder.INSTANCE.orElseThrow();
}
<T extends Keyed & RegistryElement.Inlineable<T, E, B>, E, B extends RegistryBuilder<T>> RegistryHolderSetBuilder<T, E, B> registryHolderSetBuilder(RegistryKey<T> registryKey); // TODO remove Keyed
}
@@ -12,7 +12,9 @@ import org.jetbrains.annotations.Unmodifiable;
* in the context of a {@link io.papermc.paper.registry.RegistryBuilder},
* there are methods to create them like {@link DialogRegistryEntry.Builder#registryValueSet()}.
* @param <T> registry value type
* @deprecated use {@link RegistryHolderSet} and {@link RegistryHolderSetBuilder}
*/
@Deprecated(since = "26.4", forRemoval = true)
public sealed interface RegistryValueSet<T> extends Iterable<T>, RegistrySet<T> permits RegistryValueSetImpl {
@Override
@@ -10,8 +10,10 @@ import org.jetbrains.annotations.ApiStatus;
*
* @param <API> the API type
* @param <ENTRY_BUILDER> the type of the entry builder,
* @deprecated use {@link RegistryHolderSetBuilder}
*/
@ApiStatus.NonExtendable
@Deprecated(since = "26.4", forRemoval = true)
public interface RegistryValueSetBuilder<API, ENTRY_BUILDER extends RegistryBuilder<API>> {
/**
@@ -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;
+3 -1
View File
@@ -2,7 +2,9 @@ package org.bukkit;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.PaintingVariantRegistryEntry;
import java.util.Locale;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
@@ -18,7 +20,7 @@ import org.jetbrains.annotations.Nullable;
* There may be additional arts present in the server, for example from a {@link io.papermc.paper.datapack.Datapack}
* which can be accessed via {@link RegistryAccess#registryAccess()} and {@link RegistryKey#PAINTING_VARIANT}.
*/
public interface Art extends OldEnum<Art>, Keyed {
public interface Art extends RegistryElement.Buildable<Art, PaintingVariantRegistryEntry, PaintingVariantRegistryEntry.Builder>, OldEnum<Art>, Keyed {
// Start generate - Art
Art ALBAN = getArt("alban");
+97 -15
View File
@@ -961,8 +961,7 @@ public final class Bukkit {
* Create a new explorer map targeting the closest nearby structure of a
* given {@link StructureType}.
* <br>
* This method uses implementation default values for radius and
* findUnexplored (usually 100, true).
* This method uses a radius of 100 and only searches for unexplored structures.
*
* @param world the world the map will belong to
* @param location the origin location to find the nearest structure
@@ -986,8 +985,7 @@ public final class Bukkit {
* Create a new explorer map targeting the closest nearby structure of a
* given {@link org.bukkit.generator.structure.StructureType}.
* <br>
* This method uses implementation default values for radius and
* findUnexplored (usually 100, true).
* This method uses a radius of 100 and only searches for unexplored structures.
*
* @param world the world the map will belong to
* @param location the origin location to find the nearest structure
@@ -1021,6 +1019,45 @@ public final class Bukkit {
public static @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.StructureType structureType, @NotNull org.bukkit.map.MapCursor.Type mapIcon, int radius, boolean findUnexplored) {
return server.createExplorerMap(world, location, structureType, mapIcon, radius, findUnexplored);
}
/**
* Create a new explorer map targeting the closest nearby specific
* {@link org.bukkit.generator.structure.Structure}.
* <br>
* This method uses a radius of 100 and only searches for unexplored structures.
*
* @param world the world the map will belong to
* @param location the origin location to find the nearest structure
* @param structure the specific structure to find
* @param mapIcon the map icon to use on the map
* @return a newly created item stack or null if it can't find a location
*
* @see World#locateNearestStructure(org.bukkit.Location,
* org.bukkit.generator.structure.Structure, int, boolean)
*/
public static @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.Structure structure, @NotNull org.bukkit.map.MapCursor.Type mapIcon) {
return server.createExplorerMap(world, location, structure, mapIcon);
}
/**
* Create a new explorer map targeting the closest nearby specific
* {@link org.bukkit.generator.structure.Structure}.
*
* @param world the world the map will belong to
* @param location the origin location to find the nearest structure
* @param structure the specific structure to find
* @param mapIcon the map icon to use on the map
* @param radius radius to search, see World#locateNearestStructure for more
* information
* @param findUnexplored whether to find unexplored structures
* @return the newly created item stack or null if it can't find a location
*
* @see World#locateNearestStructure(org.bukkit.Location,
* org.bukkit.generator.structure.Structure, int, boolean)
*/
public static @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.Structure structure, @NotNull org.bukkit.map.MapCursor.Type mapIcon, int radius, boolean findUnexplored) {
return server.createExplorerMap(world, location, structure, mapIcon, radius, findUnexplored);
}
// Paper end
/**
@@ -1079,7 +1116,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 +1149,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 +1166,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 +1370,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 +1393,6 @@ public final class Bukkit {
return server.removeRecipe(key);
}
// Paper start - method to resend recipes
/**
* Remove a recipe from the server.
* <p>
@@ -1335,14 +1401,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 +2732,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();
+131 -77
View File
@@ -6,10 +6,12 @@ import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import net.kyori.adventure.util.ARGBLike;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.SerializableAs;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
/**
* A container for a color palette. This class is immutable; the set methods
@@ -17,7 +19,7 @@ import org.jetbrains.annotations.Nullable;
* but subject to change.
*/
@SerializableAs("Color")
public final class Color implements ConfigurationSerializable {
public final class Color implements ConfigurationSerializable, ARGBLike {
private static final int BIT_MASK = 0xff;
private static final int DEFAULT_ALPHA = 255;
@@ -122,7 +124,12 @@ public final class Color implements ConfigurationSerializable {
* @throws IllegalArgumentException if any value is strictly {@literal >255 or <0}
*/
@NotNull
public static Color fromARGB(int alpha, int red, int green, int blue) throws IllegalArgumentException {
public static Color fromARGB(
final @Range(from = 0, to = 255) int alpha,
final @Range(from = 0, to = 255) int red,
final @Range(from = 0, to = 255) int green,
final @Range(from = 0, to = 255) int blue
) throws IllegalArgumentException {
return new Color(alpha, red, green, blue);
}
@@ -136,7 +143,11 @@ public final class Color implements ConfigurationSerializable {
* @throws IllegalArgumentException if any value is strictly {@literal >255 or <0}
*/
@NotNull
public static Color fromRGB(int red, int green, int blue) throws IllegalArgumentException {
public static Color fromRGB(
final @Range(from = 0, to = 255) int red,
final @Range(from = 0, to = 255) int green,
final @Range(from = 0, to = 255) int blue
) throws IllegalArgumentException {
return new Color(DEFAULT_ALPHA, red, green, blue);
}
@@ -150,7 +161,11 @@ public final class Color implements ConfigurationSerializable {
* @throws IllegalArgumentException if any value is strictly {@literal >255 or <0}
*/
@NotNull
public static Color fromBGR(int blue, int green, int red) throws IllegalArgumentException {
public static Color fromBGR(
final @Range(from = 0, to = 255) int blue,
final @Range(from = 0, to = 255) int green,
final @Range(from = 0, to = 255) int red
) throws IllegalArgumentException {
return new Color(DEFAULT_ALPHA, red, green, blue);
}
@@ -164,7 +179,7 @@ public final class Color implements ConfigurationSerializable {
* bits
*/
@NotNull
public static Color fromRGB(int rgb) throws IllegalArgumentException {
public static Color fromRGB(final int rgb) throws IllegalArgumentException {
Preconditions.checkArgument((rgb >> 24) == 0, "Extraneous data in: %s", rgb);
return fromRGB(rgb >> 16 & BIT_MASK, rgb >> 8 & BIT_MASK, rgb & BIT_MASK);
}
@@ -177,7 +192,7 @@ public final class Color implements ConfigurationSerializable {
* @return a new color object for specified values
*/
@NotNull
public static Color fromARGB(int argb) {
public static Color fromARGB(final int argb) {
return fromARGB(argb >> 24 & BIT_MASK, argb >> 16 & BIT_MASK, argb >> 8 & BIT_MASK, argb & BIT_MASK);
}
@@ -191,16 +206,16 @@ public final class Color implements ConfigurationSerializable {
* bits
*/
@NotNull
public static Color fromBGR(int bgr) throws IllegalArgumentException {
public static Color fromBGR(final int bgr) throws IllegalArgumentException {
Preconditions.checkArgument((bgr >> 24) == 0, "Extrenuous data in: %s", bgr);
return fromBGR(bgr >> 16 & BIT_MASK, bgr >> 8 & BIT_MASK, bgr & BIT_MASK);
}
private Color(int red, int green, int blue) {
private Color(final int red, final int green, final int blue) {
this(DEFAULT_ALPHA, red, green, blue);
}
private Color(int alpha, int red, int green, int blue) {
private Color(final int alpha, final int red, final int green, final int blue) {
Preconditions.checkArgument(alpha >= 0 && alpha <= 0xff, "Alpha[%s] is not between 0-255", alpha);
Preconditions.checkArgument(red >= 0 && red <= 0xff, "Red[%s] is not between 0-255", red);
Preconditions.checkArgument(green >= 0 && green <= 0xff, "Green[%s] is not between 0-255", green);
@@ -213,83 +228,123 @@ public final class Color implements ConfigurationSerializable {
}
/**
* Gets the alpha component
* Gets the alpha component.
*
* @return alpha component, from 0 to 255
*/
public int getAlpha() {
public @Range(from = 0, to = 255) int getAlpha() {
return BIT_MASK & alpha;
}
/**
* Creates a new Color object with specified component
* Gets the alpha component.
*
* @return alpha component, from 0 to 255
*/
@Override
public @Range(from = 0, to = 255) int alpha() {
return this.getAlpha();
}
/**
* Creates a new Color object with a specified component.
*
* @param alpha the alpha component, from 0 to 255
* @return a new color object with the alpha component
*/
@NotNull
public Color setAlpha(int alpha) {
return fromARGB(alpha, getRed(), getGreen(), getBlue());
public Color setAlpha(final @Range(from = 0, to = 255) int alpha) {
return fromARGB(alpha, this.getRed(), this.getGreen(), this.getBlue());
}
/**
* Gets the red component
* Gets the red component.
*
* @return red component, from 0 to 255
*/
public int getRed() {
return BIT_MASK & red;
public @Range(from = 0, to = 255) int getRed() {
return BIT_MASK & this.red;
}
/**
* Creates a new Color object with specified component
* Gets the red component.
*
* @return red component, from 0 to 255
*/
@Override
public @Range(from = 0, to = 255) int red() {
return this.getRed();
}
/**
* Creates a new Color object with a specified component.
*
* @param red the red component, from 0 to 255
* @return a new color object with the red component
*/
@NotNull
public Color setRed(int red) {
return fromARGB(getAlpha(), red, getGreen(), getBlue());
public Color setRed(final @Range(from = 0, to = 255) int red) {
return fromARGB(this.getAlpha(), red, this.getGreen(), this.getBlue());
}
/**
* Gets the green component
* Gets the green component.
*
* @return green component, from 0 to 255
*/
public int getGreen() {
return BIT_MASK & green;
public @Range(from = 0, to = 255) int getGreen() {
return BIT_MASK & this.green;
}
/**
* Creates a new Color object with specified component
* Gets the green component.
*
* @return green component, from 0 to 255
*/
@Override
public @Range(from = 0, to = 255) int green() {
return this.getGreen();
}
/**
* Creates a new Color object with a specified component.
*
* @param green the green component, from 0 to 255
* @return a new color object with the green component
*/
@NotNull
public Color setGreen(int green) {
return fromARGB(getAlpha(), getRed(), green, getBlue());
public Color setGreen(final @Range(from = 0, to = 255) int green) {
return fromARGB(this.getAlpha(), this.getRed(), green, this.getBlue());
}
/**
* Gets the blue component
* Gets the blue component.
*
* @return blue component, from 0 to 255
*/
public int getBlue() {
return BIT_MASK & blue;
public @Range(from = 0, to = 255) int getBlue() {
return BIT_MASK & this.blue;
}
/**
* Creates a new Color object with specified component
* Gets the blue component.
*
* @return blue component, from 0 to 255
*/
@Override
public @Range(from = 0, to = 255) int blue() {
return this.getBlue();
}
/**
* Creates a new Color object with a specified component.
*
* @param blue the blue component, from 0 to 255
* @return a new color object with the blue component
*/
@NotNull
public Color setBlue(int blue) {
return fromARGB(getAlpha(), getRed(), getGreen(), blue);
public Color setBlue(final @Range(from = 0, to = 255) int blue) {
return fromARGB(this.getAlpha(), this.getRed(), this.getGreen(), blue);
}
/**
@@ -298,7 +353,7 @@ public final class Color implements ConfigurationSerializable {
* @return An integer representation of this color, as 0xRRGGBB
*/
public int asRGB() {
return getRed() << 16 | getGreen() << 8 | getBlue();
return this.getRed() << 16 | this.getGreen() << 8 | this.getBlue();
}
/**
@@ -307,7 +362,7 @@ public final class Color implements ConfigurationSerializable {
* @return An integer representation of this color, as 0xAARRGGBB
*/
public int asARGB() {
return getAlpha() << 24 | getRed() << 16 | getGreen() << 8 | getBlue();
return this.getAlpha() << 24 | this.getRed() << 16 | this.getGreen() << 8 | this.getBlue();
}
/**
@@ -316,33 +371,33 @@ public final class Color implements ConfigurationSerializable {
* @return An integer representation of this color, as 0xBBGGRR
*/
public int asBGR() {
return getBlue() << 16 | getGreen() << 8 | getRed();
}
/**
* Creates a new color with its RGB components changed as if it was dyed
* with the colors passed in, replicating vanilla workbench dyeing
*
* @param colors The DyeColors to dye with
* @return A new color with the changed rgb components
*/
// TODO: Javadoc what this method does, not what it mimics. API != Implementation
@NotNull
public Color mixDyes(@NotNull DyeColor... colors) {
Preconditions.checkArgument(colors != null && Arrays.stream(colors).allMatch(Objects::nonNull), "DyeColor cannot be null or contain null values");
Color[] toPass = new Color[colors.length];
for (int i = 0; i < colors.length; i++) {
toPass[i] = colors[i].getColor();
}
return mixColors(toPass);
return this.getBlue() << 16 | this.getGreen() << 8 | this.getRed();
}
/**
* Creates a new color with its RGB components changed as if it was dyed
* with the colors passed in, replicating vanilla workbench dyeing.
*
* @param colors The DyeColors to dye with
* @return A new color with the changed rgb components
*/
// TODO: Javadoc what this method does, not what it mimics. API != Implementation
@NotNull
public Color mixDyes(final @NotNull DyeColor... colors) {
Preconditions.checkArgument(colors != null && Arrays.stream(colors).allMatch(Objects::nonNull), "DyeColor cannot be null or contain null values");
final Color[] toPass = new Color[colors.length];
for (int i = 0; i < colors.length; i++) {
toPass[i] = colors[i].getColor();
}
return this.mixColors(toPass);
}
/**
* Creates a new color with its RGB components changed as if it was dyed
* with the colors passed in, replicating vanilla workbench dyeing.
* <br>
* <b>Note that this method does not currently take into account alpha
* components.</b>
*
@@ -351,7 +406,7 @@ public final class Color implements ConfigurationSerializable {
*/
// TODO: Javadoc what this method does, not what it mimics. API != Implementation
@NotNull
public Color mixColors(@NotNull Color... colors) {
public Color mixColors(final @NotNull Color... colors) {
Preconditions.checkArgument(colors != null && Arrays.stream(colors).allMatch(Objects::nonNull), "Colors cannot be null");
int totalRed = this.getRed();
@@ -365,44 +420,43 @@ public final class Color implements ConfigurationSerializable {
totalMax += Math.max(Math.max(color.getRed(), color.getGreen()), color.getBlue());
}
float averageRed = totalRed / (colors.length + 1);
float averageGreen = totalGreen / (colors.length + 1);
float averageBlue = totalBlue / (colors.length + 1);
float averageMax = totalMax / (colors.length + 1);
final float averageRed = totalRed / (colors.length + 1);
final float averageGreen = totalGreen / (colors.length + 1);
final float averageBlue = totalBlue / (colors.length + 1);
final float averageMax = totalMax / (colors.length + 1);
float maximumOfAverages = Math.max(Math.max(averageRed, averageGreen), averageBlue);
float gainFactor = averageMax / maximumOfAverages;
final float maximumOfAverages = Math.max(Math.max(averageRed, averageGreen), averageBlue);
final float gainFactor = averageMax / maximumOfAverages;
return Color.fromRGB((int) (averageRed * gainFactor), (int) (averageGreen * gainFactor), (int) (averageBlue * gainFactor));
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Color)) {
public boolean equals(final Object o) {
if (!(o instanceof Color that)) {
return false;
}
final Color that = (Color) o;
return this.alpha == that.alpha && this.blue == that.blue && this.green == that.green && this.red == that.red;
}
@Override
public int hashCode() {
return asARGB() ^ Color.class.hashCode();
return this.asARGB() ^ Color.class.hashCode();
}
@Override
@NotNull
public Map<String, Object> serialize() {
return ImmutableMap.of(
"ALPHA", getAlpha(),
"RED", getRed(),
"BLUE", getBlue(),
"GREEN", getGreen()
"ALPHA", this.getAlpha(),
"RED", this.getRed(),
"BLUE", this.getBlue(),
"GREEN", this.getGreen()
);
}
@NotNull
public static Color deserialize(@NotNull Map<String, Object> map) {
public static Color deserialize(final @NotNull Map<String, Object> map) {
return fromARGB(
asInt("ALPHA", map, DEFAULT_ALPHA),
asInt("RED", map),
@@ -411,23 +465,23 @@ public final class Color implements ConfigurationSerializable {
);
}
private static int asInt(@NotNull String string, @NotNull Map<String, Object> map) {
private static int asInt(final @NotNull String string, final @NotNull Map<String, Object> map) {
return asInt(string, map, null);
}
private static int asInt(@NotNull String string, @NotNull Map<String, Object> map, @Nullable Object defaultValue) {
Object value = map.getOrDefault(string, defaultValue);
private static int asInt(final @NotNull String string, final @NotNull Map<String, Object> map, final @Nullable Object defaultValue) {
final Object value = map.getOrDefault(string, defaultValue);
if (value == null) {
throw new IllegalArgumentException(string + " not in map " + map);
}
if (!(value instanceof Number)) {
if (!(value instanceof Number number)) {
throw new IllegalArgumentException(string + '(' + value + ") is not a number");
}
return ((Number) value).intValue();
return number.intValue();
}
@Override
public String toString() {
return "Color:[argb0x" + Integer.toHexString(asARGB()).toUpperCase(Locale.ROOT) + "]";
return "Color:[argb0x" + Integer.toHexString(this.asARGB()).toUpperCase(Locale.ROOT) + "]";
}
}
@@ -1,6 +1,7 @@
package org.bukkit;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import java.util.Locale;
import net.kyori.adventure.key.Key;
@@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents a fluid type.
*/
public interface Fluid extends OldEnum<Fluid>, Keyed {
public interface Fluid extends RegistryElement<Fluid>, OldEnum<Fluid>, Keyed {
// Start generate - Fluid
Fluid EMPTY = getFluid("empty");
@@ -1,5 +1,7 @@
package org.bukkit;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.data.GameEventRegistryEntry;
import java.util.Collection;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
@@ -9,7 +11,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a generic Mojang game event.
*/
public abstract class GameEvent implements Keyed {
public abstract class GameEvent implements RegistryElement.Buildable<GameEvent, GameEventRegistryEntry, GameEventRegistryEntry.Builder>, Keyed {
// Start generate - GameEvent
public static final GameEvent BLOCK_ACTIVATE = getEvent("block_activate");
@@ -2,6 +2,7 @@ package org.bukkit;
import com.google.common.base.Preconditions;
import io.papermc.paper.InternalAPIBridge;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.world.flag.FeatureDependant;
import java.util.function.UnaryOperator;
import org.jetbrains.annotations.ApiStatus;
@@ -19,7 +20,7 @@ import org.jetbrains.annotations.Nullable;
* @see GameRules
*/
@ApiStatus.NonExtendable
public abstract class GameRule<T> implements net.kyori.adventure.translation.Translatable, FeatureDependant, Keyed {
public abstract class GameRule<T> implements RegistryElement<GameRule<T>>, net.kyori.adventure.translation.Translatable, FeatureDependant, Keyed {
// Boolean rules
/**
@@ -1,7 +1,9 @@
package org.bukkit;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.JukeboxSongRegistryEntry;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import net.kyori.adventure.text.Component;
@@ -11,7 +13,7 @@ import org.jspecify.annotations.NullMarked;
* Represents a song which may play in a Jukebox.
*/
@NullMarked
public interface JukeboxSong extends Keyed, Translatable {
public interface JukeboxSong extends RegistryElement.Buildable<JukeboxSong, JukeboxSongRegistryEntry, JukeboxSongRegistryEntry.Builder>, Keyed, Translatable {
// Start generate - JukeboxSong
JukeboxSong ELEVEN = get("11");
@@ -2,9 +2,10 @@ package org.bukkit;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.InlinedRegistryBuilderProvider;
import io.papermc.paper.registry.data.InstrumentRegistryEntry;
import io.papermc.paper.registry.data.RegistryBuilderProvider;
import java.util.Collection;
import java.util.function.Consumer;
import net.kyori.adventure.key.Key;
@@ -14,7 +15,7 @@ import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public abstract class MusicInstrument implements Keyed, net.kyori.adventure.translation.Translatable {
public abstract class MusicInstrument implements RegistryElement.Inlineable<MusicInstrument, InstrumentRegistryEntry, InstrumentRegistryEntry.Builder>, Keyed, net.kyori.adventure.translation.Translatable {
/**
* Creates an inlined music instrument.
@@ -23,7 +24,7 @@ public abstract class MusicInstrument implements Keyed, net.kyori.adventure.tran
* @return the created music instrument
*/
public static MusicInstrument create(final Consumer<RegistryBuilderFactory<MusicInstrument, ? extends InstrumentRegistryEntry.Builder>> value) {
return InlinedRegistryBuilderProvider.instance().createInstrument(value);
return RegistryBuilderProvider.instance().create(RegistryKey.INSTRUMENT, value);
}
// Start generate - MusicInstrument
@@ -500,7 +500,6 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
* @throws NoSuchElementException if no tag with the given key is found
* @throws UnsupportedOperationException if this registry doesn't have or support tags
* @see #hasTag(TagKey)
* @see #getTagValues(TagKey)
*/
Tag<T> getTag(TagKey<T> key);
@@ -512,11 +511,13 @@ public interface Registry<T extends Keyed> extends Iterable<T> {
* @throws NoSuchElementException if no tag with the given key is found
* @throws UnsupportedOperationException if this registry doesn't have or support tags
* @see #getTag(TagKey)
* @see Tag#resolve(Registry)
* @see Tag#resolve()
* @deprecated use {@link Tag#resolve()} on the fetched tag
*/
@Deprecated(since = "26.4", forRemoval = true)
default Collection<T> getTagValues(final TagKey<T> key) {
Tag<T> tag = this.getTag(key);
return tag.resolve(this);
final Tag<T> tag = this.getTag(key);
return tag.resolve();
}
/**
+108 -36
View File
@@ -61,6 +61,8 @@ import org.bukkit.profile.PlayerProfile;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scoreboard.Criteria;
import org.bukkit.scoreboard.ScoreboardManager;
import org.bukkit.generator.structure.Structure;
import org.bukkit.map.MapCursor;
import org.bukkit.structure.StructureManager;
import org.bukkit.util.CachedServerIcon;
import org.jetbrains.annotations.ApiStatus;
@@ -872,8 +874,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
* Create a new explorer map targeting the closest nearby structure of a
* given {@link StructureType}.
* <br>
* This method uses implementation default values for radius and
* findUnexplored (usually 100, true).
* This method uses a radius of 100 and only searches for unexplored structures.
*
* @param world the world the map will belong to
* @param location the origin location to find the nearest structure
@@ -882,9 +883,9 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*
* @see World#locateNearestStructure(org.bukkit.Location,
* org.bukkit.StructureType, int, boolean)
* @deprecated use {@link #createExplorerMap(World, Location, org.bukkit.generator.structure.StructureType, org.bukkit.map.MapCursor.Type)}
* @deprecated use {@link #createExplorerMap(World, Location, org.bukkit.generator.structure.StructureType, MapCursor.Type)}
*/
@Deprecated // Paper
@Deprecated
@NotNull
default ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull StructureType structureType) {
return this.createExplorerMap(world, location, structureType, 100, true);
@@ -904,18 +905,16 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*
* @see World#locateNearestStructure(org.bukkit.Location,
* org.bukkit.StructureType, int, boolean)
* @deprecated use {@link #createExplorerMap(World, Location, org.bukkit.generator.structure.StructureType, org.bukkit.map.MapCursor.Type, int, boolean)}
* @deprecated use {@link #createExplorerMap(World, Location, org.bukkit.generator.structure.StructureType, MapCursor.Type, int, boolean)}
*/
@Deprecated // Paper
@NotNull
public ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull StructureType structureType, int radius, boolean findUnexplored);
// Paper start
@Deprecated
@NotNull ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull StructureType structureType, int radius, boolean findUnexplored);
/**
* Create a new explorer map targeting the closest nearby structure of a
* given {@link org.bukkit.generator.structure.StructureType}.
* given {@link StructureType}.
* <br>
* This method uses implementation default values for radius and
* findUnexplored (usually 100, true).
* This method uses a radius of 100 and only searches for unexplored structures.
*
* @param world the world the map will belong to
* @param location the origin location to find the nearest structure
@@ -924,15 +923,15 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
* @return a newly created item stack or null if it can't find a location
*
* @see World#locateNearestStructure(org.bukkit.Location,
* org.bukkit.generator.structure.StructureType, int, boolean)
* StructureType, int, boolean)
*/
default @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.StructureType structureType, @NotNull org.bukkit.map.MapCursor.Type mapIcon) {
default @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.StructureType structureType, @NotNull MapCursor.Type mapIcon) {
return this.createExplorerMap(world, location, structureType, mapIcon, 100, true);
}
/**
* Create a new explorer map targeting the closest nearby structure of a
* given {@link org.bukkit.generator.structure.StructureType}.
* given {@link StructureType}.
*
* @param world the world the map will belong to
* @param location the origin location to find the nearest structure
@@ -944,10 +943,46 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
* @return the newly created item stack or null if it can't find a location
*
* @see World#locateNearestStructure(org.bukkit.Location,
* org.bukkit.generator.structure.StructureType, int, boolean)
* StructureType, int, boolean)
*/
@Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.StructureType structureType, @NotNull org.bukkit.map.MapCursor.Type mapIcon, int radius, boolean findUnexplored);
// Paper end
@Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull org.bukkit.generator.structure.StructureType structureType, @NotNull MapCursor.Type mapIcon, int radius, boolean findUnexplored);
/**
* Create a new explorer map targeting the closest nearby specific
* {@link Structure}.
* <br>
* This method uses a radius of 100 and only searches for unexplored structures.
*
* @param world the world the map will belong to
* @param location the origin location to find the nearest structure
* @param structure the specific structure to find
* @param mapIcon the map icon to use on the map
* @return a newly created item stack or null if it can't find a location
*
* @see World#locateNearestStructure(org.bukkit.Location,
* Structure, int, boolean)
*/
default @Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull Structure structure, @NotNull MapCursor.Type mapIcon) {
return this.createExplorerMap(world, location, structure, mapIcon, 100, true);
}
/**
* Create a new explorer map targeting the closest nearby specific
* {@link Structure}.
*
* @param world the world the map will belong to
* @param location the origin location to find the nearest structure
* @param structure the specific structure to find
* @param mapIcon the map icon to use on the map
* @param radius radius to search, see World#locateNearestStructure for more
* information
* @param findUnexplored whether to find unexplored structures
* @return the newly created item stack or null if it can't find a location
*
* @see World#locateNearestStructure(org.bukkit.Location,
* Structure, int, boolean)
*/
@Nullable ItemStack createExplorerMap(@NotNull World world, @NotNull Location location, @NotNull Structure structure, @NotNull MapCursor.Type mapIcon, int radius, boolean findUnexplored);
/**
* Reloads the server, refreshing settings and plugin information.
@@ -995,7 +1030,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 +1057,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 +1256,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 +1274,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 +1284,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.
@@ -2207,7 +2275,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();
+42 -9
View File
@@ -1,12 +1,18 @@
package org.bukkit;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.RegistryBuilderProvider;
import io.papermc.paper.registry.data.SoundEventRegistryEntry;
import java.util.Locale;
import java.util.function.Consumer;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.util.OldEnum;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* An interface of Sounds the server is able to send to players.
@@ -21,7 +27,29 @@ import org.jetbrains.annotations.NotNull;
* guarantee values will not be removed from this interface. As such, you should not
* depend on the ordinal values of this class.
*/
public interface Sound extends OldEnum<Sound>, Keyed, net.kyori.adventure.sound.Sound.Type { // Paper - implement Sound.Type
@NullMarked
public interface Sound extends RegistryElement.Inlineable<Sound, SoundEventRegistryEntry, SoundEventRegistryEntry.Builder>, OldEnum<Sound>, Keyed, net.kyori.adventure.sound.Sound.Type { // Paper - implement Sound.Type
/**
* Creates an inlined sound event.
*
* @param key the sound location
* @param range an optional fixed range
* @return the created sound
*/
static Sound create(final Key key, final @Nullable Float range) {
return create(builder -> builder.empty().location(key).fixedRange(range));
}
/**
* Creates an inlined sound event.
*
* @param value a consumer for the builder factory
* @return the created sound
*/
static Sound create(final Consumer<RegistryBuilderFactory<Sound, ? extends SoundEventRegistryEntry.Builder>> value) {
return RegistryBuilderProvider.instance().create(RegistryKey.SOUND_EVENT, value);
}
// Start generate - Sound
Sound AMBIENT_BASALT_DELTAS_ADDITIONS = getSound("ambient.basalt_deltas.additions");
@@ -4007,8 +4035,7 @@ public interface Sound extends OldEnum<Sound>, Keyed, net.kyori.adventure.sound.
Sound WEATHER_RAIN_ABOVE = getSound("weather.rain.above");
// End generate - Sound
@NotNull
private static Sound getSound(@NotNull @KeyPattern.Value String key) {
private static Sound getSound(@KeyPattern.Value String key) {
return Registry.SOUNDS.getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key));
}
@@ -4017,9 +4044,8 @@ public interface Sound extends OldEnum<Sound>, Keyed, net.kyori.adventure.sound.
* @return the sound with the given name.
* @deprecated only for backwards compatibility, use {@link Registry#get(NamespacedKey)} instead.
*/
@NotNull
@Deprecated(since = "1.21.3", forRemoval = true) @org.jetbrains.annotations.ApiStatus.ScheduledForRemoval(inVersion = "1.22") // Paper - will be removed via asm-utils
static Sound valueOf(@NotNull String name) {
static Sound valueOf(String name) {
final NamespacedKey key = NamespacedKey.fromString(name.toLowerCase(Locale.ROOT));
Sound sound;
@@ -4049,14 +4075,21 @@ public interface Sound extends OldEnum<Sound>, Keyed, net.kyori.adventure.sound.
*/
@Deprecated(since = "1.20.5", forRemoval = true)
@Override
@NotNull NamespacedKey getKey();
NamespacedKey getKey();
// Paper end - deprecate getKey
/**
* Gets the fixed range, a radius in block which this sound is audible.
* The computed range will depends on the volume when this value is not defined.
*
* @return the fixed range
*/
@Nullable Float getFixedRange();
/**
* @return an array of all known sounds.
* @deprecated use {@link Registry#stream()}.
*/
@NotNull
@Deprecated(since = "1.21.3", forRemoval = true) @org.jetbrains.annotations.ApiStatus.ScheduledForRemoval(inVersion = "1.22") // Paper - will be removed via asm-utils
static Sound[] values() {
return Registry.SOUNDS.stream().toArray(Sound[]::new);
@@ -4069,7 +4102,7 @@ public interface Sound extends OldEnum<Sound>, Keyed, net.kyori.adventure.sound.
*/
@Deprecated(since = "1.20.5", forRemoval = true)
@Override
default net.kyori.adventure.key.@NotNull Key key() {
default net.kyori.adventure.key.Key key() {
return this.getKey();
}
// Paper end
@@ -110,7 +110,15 @@ public enum TreeType {
*/
PALE_OAK_CREAKING,
/**
* Poplar tree
* Poplar tree with red leaves
*/
POPLAR,
RED_POPLAR,
/**
* Poplar tree with orange leaves
*/
ORANGE_POPLAR,
/**
* Poplar tree with yellow leaves
*/
YELLOW_POPLAR,
}
@@ -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
@@ -1,6 +1,7 @@
package org.bukkit.attribute;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import java.util.Locale;
import net.kyori.adventure.key.Key;
@@ -17,7 +18,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Types of attributes which may be present on an {@link Attributable}.
*/
public interface Attribute extends OldEnum<Attribute>, Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - Adventure translations
public interface Attribute extends RegistryElement<Attribute>, OldEnum<Attribute>, Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - Adventure translations
/**
* Maximum health of an Entity.
@@ -3,6 +3,7 @@ package org.bukkit.block;
import com.google.common.base.Preconditions;
import io.papermc.paper.InternalAPIBridge;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import java.util.Locale;
import net.kyori.adventure.key.Key;
@@ -23,7 +24,7 @@ import org.jetbrains.annotations.NotNull;
* There may be additional biomes present in the server, for example from a {@link io.papermc.paper.datapack.Datapack}
* which can be accessed via {@link io.papermc.paper.registry.RegistryAccess#getRegistry(RegistryKey)} and {@link RegistryKey#BIOME}.
*/
public interface Biome extends OldEnum<Biome>, Keyed, net.kyori.adventure.translation.Translatable { // Paper - Adventure translations
public interface Biome extends RegistryElement<Biome>, OldEnum<Biome>, Keyed, net.kyori.adventure.translation.Translatable { // Paper - Adventure translations
// Start generate - Biome
Biome BADLANDS = getBiome("badlands");
@@ -1,9 +1,11 @@
package org.bukkit.block;
import io.papermc.paper.registry.RegistryElement;
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;
@@ -134,7 +136,7 @@ import org.jspecify.annotations.Nullable;
* Represents a block type.
*/
@NullMarked
public interface BlockType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - add translatable & feature flag API
public interface BlockType extends RegistryElement<BlockType>, Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - add translatable & feature flag API
/**
* Typed represents a subtype of {@link BlockType}s that have a known block
@@ -2986,4 +2988,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();
}
@@ -1,27 +1,34 @@
package org.bukkit.block;
import io.papermc.paper.math.Position;
import java.util.List;
import java.util.UUID;
import net.kyori.adventure.text.Component;
import org.bukkit.DyeColor;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.material.Colorable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* Represents a captured state of either a SignPost or a WallSign.
*/
@NullMarked
public interface Sign extends TileState, Colorable {
// Paper start
/**
* Gets all the lines of text currently on the {@link Side#FRONT} of this sign.
*
* @return List of components containing each line of text
* @deprecated A sign may have multiple writable sides now. Use {@link Sign#getSide(Side)} and {@link SignSide#lines()}.
*/
@NotNull
@Deprecated
public java.util.List<net.kyori.adventure.text.Component> lines();
@Deprecated(since = "1.20")
default List<Component> lines() {
return this.getSide(Side.FRONT).lines();
}
/**
* Gets the line of text at the specified index on the {@link Side#FRONT}.
@@ -29,13 +36,14 @@ public interface Sign extends TileState, Colorable {
* For example, getLine(0) will return the first line of text.
*
* @param index Line number to get the text from, starting at 0
* @throws IndexOutOfBoundsException Thrown when the line does not exist
* @return Text on the given line
* @throws IndexOutOfBoundsException Thrown when the line does not exist
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int)}.
*/
@NotNull
@Deprecated
public net.kyori.adventure.text.Component line(int index) throws IndexOutOfBoundsException;
@Deprecated(since = "1.20")
default Component line(int index) throws IndexOutOfBoundsException {
return this.getSide(Side.FRONT).line(index);
}
/**
* Sets the line of text at the specified index on the {@link Side#FRONT}.
@@ -44,23 +52,25 @@ public interface Sign extends TileState, Colorable {
* "Line One".
*
* @param index Line number to set the text at, starting from 0
* @param line New text to set at the specified index
* @param line New text to set at the specified index
* @throws IndexOutOfBoundsException If the index is out of the range 0..3
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int, net.kyori.adventure.text.Component)}.
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int, Component)}.
*/
@Deprecated
public void line(int index, net.kyori.adventure.text.@NotNull Component line) throws IndexOutOfBoundsException;
// Paper end
@Deprecated(since = "1.20")
default void line(int index, Component line) throws IndexOutOfBoundsException {
this.getSide(Side.FRONT).line(index, line);
}
/**
* Gets all the lines of text currently on the {@link Side#FRONT} of this sign.
*
* @return Array of Strings containing each line of text
* @deprecated A sign may have multiple writable sides now. Use {@link Sign#getSide(Side)} and {@link SignSide#lines()}.
* @deprecated A sign may have multiple writable sides now. Use {@link Sign#getSide(Side)} and {@link SignSide#lines()}.
*/
@Deprecated(since = "1.20")
@NotNull
public String[] getLines();
default String[] getLines() {
return this.getSide(Side.FRONT).getLines();
}
/**
* Gets the line of text at the specified index.
@@ -73,8 +83,9 @@ public interface Sign extends TileState, Colorable {
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int)}.
*/
@Deprecated(since = "1.20")
@NotNull
public String getLine(int index) throws IndexOutOfBoundsException;
default String getLine(int index) throws IndexOutOfBoundsException {
return this.getSide(Side.FRONT).getLine(index);
}
/**
* Sets the line of text at the specified index.
@@ -83,12 +94,14 @@ public interface Sign extends TileState, Colorable {
* "Line One".
*
* @param index Line number to set the text at, starting from 0
* @param line New text to set at the specified index
* @param line New text to set at the specified index
* @throws IndexOutOfBoundsException If the index is out of the range 0..3
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#line(int, net.kyori.adventure.text.Component)}.
*/
@Deprecated(since = "1.20")
public void setLine(int index, @NotNull String line) throws IndexOutOfBoundsException;
default void setLine(int index, String line) throws IndexOutOfBoundsException {
this.getSide(Side.FRONT).setLine(index, line);
}
/**
* Marks whether this sign can be edited by players.
@@ -97,7 +110,9 @@ public interface Sign extends TileState, Colorable {
* @deprecated use {@link #isWaxed()} instead
*/
@Deprecated(since = "1.20.1")
public boolean isEditable();
default boolean isEditable() {
return !this.isWaxed();
}
/**
* Marks whether this sign can be edited by players.
@@ -106,7 +121,9 @@ public interface Sign extends TileState, Colorable {
* @deprecated use {@link #setWaxed(boolean)} instead
*/
@Deprecated(since = "1.20.1")
public void setEditable(boolean editable);
default void setEditable(boolean editable) {
this.setWaxed(!editable);
}
/**
* Gets whether or not this sign has been waxed. If a sign has been waxed, it
@@ -114,7 +131,7 @@ public interface Sign extends TileState, Colorable {
*
* @return if this sign is waxed
*/
public boolean isWaxed();
boolean isWaxed();
/**
* Sets whether or not this sign has been waxed. If a sign has been waxed, it
@@ -122,7 +139,7 @@ public interface Sign extends TileState, Colorable {
*
* @param waxed if this sign is waxed
*/
public void setWaxed(boolean waxed);
void setWaxed(boolean waxed);
/**
* Gets whether this sign has glowing text. Only affects the {@link Side#FRONT}.
@@ -131,7 +148,9 @@ public interface Sign extends TileState, Colorable {
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#isGlowingText()}.
*/
@Deprecated(since = "1.20")
public boolean isGlowingText();
default boolean isGlowingText() {
return this.getSide(Side.FRONT).isGlowingText();
}
/**
* Sets whether this sign has glowing text. Only affects the {@link Side#FRONT}.
@@ -140,17 +159,20 @@ public interface Sign extends TileState, Colorable {
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#setGlowingText(boolean)}.
*/
@Deprecated(since = "1.20")
public void setGlowingText(boolean glowing);
default void setGlowingText(boolean glowing) {
this.getSide(Side.FRONT).setGlowingText(glowing);
}
/**
* {@inheritDoc}
*
* @deprecated A sign may have multiple writable sides now. Use {@link #getSide(Side)} and {@link SignSide#getColor()}.
*/
@NotNull
@Override
@Deprecated(since = "1.20")
public DyeColor getColor();
default @Nullable DyeColor getColor() {
return this.getSide(Side.FRONT).getColor();
}
/**
* {@inheritDoc}
@@ -159,7 +181,9 @@ public interface Sign extends TileState, Colorable {
*/
@Override
@Deprecated(since = "1.20")
public void setColor(@NotNull DyeColor color);
default void setColor(DyeColor color) {
this.getSide(Side.FRONT).setColor(color);
}
/**
* Return the side of the sign.
@@ -167,8 +191,7 @@ public interface Sign extends TileState, Colorable {
* @param side the side of the sign
* @return the selected side of the sign
*/
@NotNull
public SignSide getSide(@NotNull Side side);
SignSide getSide(Side side);
/**
* Gets the side of this sign the given player is currently standing on.
@@ -176,8 +199,7 @@ public interface Sign extends TileState, Colorable {
* @param player the player
* @return the side the player is standing on
*/
@NotNull
public SignSide getTargetSide(@NotNull Player player);
SignSide getTargetSide(Player player);
/**
* Gets the player that is currently allowed to edit this sign. <br>
@@ -187,16 +209,15 @@ public interface Sign extends TileState, Colorable {
*
* @return the player allowed to edit this sign, or null
*/
@Nullable
public Player getAllowedEditor();
// Paper start - More Sign Block API
@Nullable Player getAllowedEditor();
/**
* Gets the allowed editor's UUID.
* <br>Edits from other players will be rejected if this value is not null.
*
* @return the allowed editor's UUID, or null
*/
@Nullable java.util.UUID getAllowedEditorUniqueId();
@Nullable UUID getAllowedEditorUniqueId();
/**
* Sets the allowed editor's UUID.
@@ -205,7 +226,7 @@ public interface Sign extends TileState, Colorable {
*
* @param uuid the allowed editor's UUID
*/
void setAllowedEditorUniqueId(@Nullable java.util.UUID uuid);
void setAllowedEditorUniqueId(@Nullable UUID uuid);
/**
* Compute the side facing the specified entity.
@@ -213,7 +234,7 @@ public interface Sign extends TileState, Colorable {
* @param entity the entity
* @return the side it is facing
*/
default @NotNull Side getInteractableSideFor(org.bukkit.entity.@NotNull Entity entity) {
default Side getInteractableSideFor(final Entity entity) {
return this.getInteractableSideFor(entity.getLocation());
}
@@ -223,7 +244,7 @@ public interface Sign extends TileState, Colorable {
* @param position the position
* @return the side the position is facing
*/
default @NotNull Side getInteractableSideFor(io.papermc.paper.math.@NotNull Position position) {
default Side getInteractableSideFor(final Position position) {
return this.getInteractableSideFor(position.x(), position.z());
}
@@ -234,6 +255,20 @@ public interface Sign extends TileState, Colorable {
* @param z the z coord
* @return the side the coordinates are facing
*/
@NotNull Side getInteractableSideFor(double x, double z);
// Paper end - More Sign Block API
Side getInteractableSideFor(double x, double z);
/**
* Gets whether operator features are allowed on this sign.
* When enabled, supported click events in the root component of each line are executed when the relevant side is used.
*
* @return if operator features are allowed
*/
boolean isOperatorFeaturesAllowed();
/**
* Sets whether operator features are allowed on this sign.
*
* @param allowed whether operator features are allowed
*/
void setOperatorFeaturesAllowed(boolean allowed);
}
@@ -2,7 +2,9 @@ package org.bukkit.block.banner;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.BannerPatternRegistryEntry;
import java.util.Locale;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
@@ -14,7 +16,7 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface PatternType extends OldEnum<PatternType>, Keyed {
public interface PatternType extends RegistryElement.Buildable<PatternType, BannerPatternRegistryEntry, BannerPatternRegistryEntry.Builder>, OldEnum<PatternType>, Keyed {
// Start generate - PatternType
PatternType BASE = getType("base");
Loaded 100 of 485 files, more files were not shown because too many files have changed in this diff. Show more