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
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
284 changed files with 5195 additions and 2396 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"/>
+3
View File
@@ -477,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
@@ -583,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
@@ -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,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 {
}
@@ -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));
}
}
@@ -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
@@ -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;
}
}
@@ -1,6 +1,7 @@
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;
@@ -177,6 +178,23 @@ public interface PersistentDataContainerView {
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,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>> {
/**
+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");
+41 -4
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
/**
+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();
}
/**
+53 -18
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.
+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,
}
@@ -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,5 +1,6 @@
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;
@@ -135,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
@@ -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");
@@ -1,7 +1,9 @@
package org.bukkit.damage;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.DamageTypeRegistryEntry;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Keyed;
@@ -17,7 +19,7 @@ import org.jetbrains.annotations.NotNull;
*
* @see <a href="https://minecraft.wiki/w/Damage_type">Minecraft Wiki</a>
*/
public interface DamageType extends Keyed, Translatable {
public interface DamageType extends RegistryElement.Buildable<DamageType, DamageTypeRegistryEntry, DamageTypeRegistryEntry.Builder>, Keyed, Translatable {
// Start generate - DamageType
DamageType ARROW = getDamageType("arrow");
@@ -1,7 +1,9 @@
package org.bukkit.enchantments;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.EnchantmentRegistryEntry;
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;
/**
* The various type of enchantments that may be added to armour or weapons
*/
public abstract class Enchantment implements Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - Adventure translations
public abstract class Enchantment implements RegistryElement.Buildable<Enchantment, EnchantmentRegistryEntry, EnchantmentRegistryEntry.Builder>, Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - Adventure translations
/**
* Provides protection against environmental damage
*/
@@ -2,7 +2,9 @@ package org.bukkit.entity;
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.CatTypeRegistryEntry;
import java.util.Locale;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
@@ -68,7 +70,7 @@ public interface Cat extends Tameable, Sittable, io.papermc.paper.entity.CollarC
/**
* Represents the various different cat types there are.
*/
interface Type extends OldEnum<Type>, Keyed {
interface Type extends RegistryElement.Buildable<Type, CatTypeRegistryEntry, CatTypeRegistryEntry.Builder>, OldEnum<Type>, Keyed {
// Start generate - CatType
Type ALL_BLACK = getType("all_black");
@@ -127,7 +129,7 @@ public interface Cat extends Tameable, Sittable, io.papermc.paper.entity.CollarC
/**
* Represents the sound variant of a cat.
*/
interface SoundVariant extends Keyed {
interface SoundVariant extends RegistryElement<SoundVariant>, Keyed {
// Start generate - CatSoundVariant
SoundVariant CLASSIC = getSoundVariant("classic");
@@ -1,7 +1,9 @@
package org.bukkit.entity;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.ChickenVariantRegistryEntry;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Keyed;
@@ -72,7 +74,7 @@ public interface Chicken extends Animals {
/**
* Represents the variant of a chicken.
*/
interface Variant extends Keyed {
interface Variant extends RegistryElement.Buildable<Variant, ChickenVariantRegistryEntry, ChickenVariantRegistryEntry.Builder>, Keyed {
// Start generate - ChickenVariant
Variant COLD = getVariant("cold");
@@ -90,7 +92,7 @@ public interface Chicken extends Animals {
/**
* Represents the sound variant of a chicken.
*/
interface SoundVariant extends Keyed {
interface SoundVariant extends RegistryElement<SoundVariant>, Keyed {
// Start generate - ChickenSoundVariant
SoundVariant CLASSIC = getSoundVariant("classic");
@@ -1,7 +1,9 @@
package org.bukkit.entity;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.CowVariantRegistryEntry;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Keyed;
@@ -44,7 +46,7 @@ public interface Cow extends AbstractCow {
/**
* Represents the variant of a cow.
*/
interface Variant extends Keyed {
interface Variant extends RegistryElement.Buildable<Variant, CowVariantRegistryEntry, CowVariantRegistryEntry.Builder>, Keyed {
// Start generate - CowVariant
Variant COLD = getVariant("cold");
@@ -62,7 +64,7 @@ public interface Cow extends AbstractCow {
/**
* Represents the sound variant of a cow.
*/
interface SoundVariant extends Keyed {
interface SoundVariant extends RegistryElement<SoundVariant>, Keyed {
// Start generate - CowSoundVariant
SoundVariant CLASSIC = getSoundVariant("classic");
@@ -2,7 +2,9 @@ package org.bukkit.entity;
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.FrogVariantRegistryEntry;
import java.util.Locale;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
@@ -51,7 +53,7 @@ public interface Frog extends Animals {
/**
* Represents the variant of a frog - ie its color.
*/
interface Variant extends OldEnum<Variant>, Keyed {
interface Variant extends RegistryElement.Buildable<Variant, FrogVariantRegistryEntry, FrogVariantRegistryEntry.Builder>, OldEnum<Variant>, Keyed {
// Start generate - FrogVariant
Variant COLD = getVariant("cold");
@@ -1,5 +1,6 @@
package org.bukkit.entity;
import com.google.common.base.Preconditions;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -1401,25 +1402,39 @@ public interface LivingEntity extends Attributable, Damageable, ProjectileSource
void setHurtDirection(float hurtDirection);
// Paper end - hurt direction API
// Paper start - swing hand API
/**
* Makes this entity swing their hand.
* Makes this entity swing their hand, using the {@link io.papermc.paper.datacomponent.DataComponentTypes#ATTACK_ANIMATION}
* component on the item held in that hand to determine the animation type and duration.
* If the held item does not have the component, the default {@link io.papermc.paper.datacomponent.item.SwingAnimation.Animation#WHACK}
* animation with a duration of {@code 6} ticks is used.
*
* <p>This method does nothing if this entity does not
* have an animation for swinging their hand.
*
* @param hand hand to be swung, either {@link org.bukkit.inventory.EquipmentSlot#HAND} or {@link org.bukkit.inventory.EquipmentSlot#OFF_HAND}
* @throws IllegalArgumentException if invalid hand is passed
* @throws IllegalArgumentException if an invalid hand is passed
*/
default void swingHand(@NotNull org.bukkit.inventory.EquipmentSlot hand) {
com.google.common.base.Preconditions.checkArgument(hand != null && hand.isHand(), String.format("Expected a valid hand, got \"%s\" instead!", hand));
Preconditions.checkArgument(hand != null && hand.isHand(), "Expected a valid hand, got \"%s\" instead!", hand);
if (hand == org.bukkit.inventory.EquipmentSlot.HAND) {
this.swingMainHand();
} else {
this.swingOffHand();
}
}
// Paper end - swing hand API
/**
* Makes this entity swing their hand using the provided {@link io.papermc.paper.datacomponent.item.SwingAnimation},
* instead of the swing animation component the held item may have.
*
* <p>This method does nothing if this entity does not
* have an animation for swinging their hand.
*
* @param hand hand to be swung, either {@link org.bukkit.inventory.EquipmentSlot#HAND} or {@link org.bukkit.inventory.EquipmentSlot#OFF_HAND}
* @param swingAnimation the swing animation to use
* @throws IllegalArgumentException if an invalid hand is passed
*/
void swingHand(@NotNull org.bukkit.inventory.EquipmentSlot hand, @NotNull io.papermc.paper.datacomponent.item.SwingAnimation swingAnimation);
// Paper start - knockback API
/**
@@ -1,7 +1,9 @@
package org.bukkit.entity;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.PigVariantRegistryEntry;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Keyed;
@@ -44,7 +46,7 @@ public interface Pig extends Steerable, Vehicle {
/**
* Represents the variant of a pig.
*/
interface Variant extends Keyed {
interface Variant extends RegistryElement.Buildable<Variant, PigVariantRegistryEntry, PigVariantRegistryEntry.Builder>, Keyed {
// Start generate - PigVariant
Variant COLD = getVariant("cold");
@@ -62,7 +64,7 @@ public interface Pig extends Steerable, Vehicle {
/**
* Represents the sound variant of a pig.
*/
interface SoundVariant extends Keyed {
interface SoundVariant extends RegistryElement<SoundVariant>, Keyed {
// Start generate - PigSoundVariant
SoundVariant BIG = getSoundVariant("big");
@@ -5,7 +5,9 @@ import io.papermc.paper.entity.Bucketable;
import io.papermc.paper.entity.Shearable;
import io.papermc.paper.event.entity.EntityEquipmentChangedEvent;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.SulfurCubeArchetypeRegistryEntry;
import io.papermc.paper.registry.keys.tags.ItemTypeTagKeys;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
@@ -135,7 +137,7 @@ public interface SulfurCube extends AbstractCubeMob, Shearable, Bucketable, Agea
* which define a lot of possible behavior and interaction
* throughout its lifetime.
*/
interface Archetype extends Keyed {
interface Archetype extends RegistryElement.Buildable<Archetype, SulfurCubeArchetypeRegistryEntry, SulfurCubeArchetypeRegistryEntry.Builder>, Keyed {
// Start generate - SulfurCubeArchetype
Archetype BOUNCY = getArchetype("bouncy");
@@ -1,9 +1,10 @@
package org.bukkit.entity;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryElement;
import java.util.Locale;
import java.util.Map; // Paper
import java.util.UUID; // Paper
import java.util.Map;
import java.util.UUID;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Keyed;
@@ -166,7 +167,7 @@ public interface Villager extends AbstractVillager {
* Represents Villager type, usually corresponding to what biome they spawn
* in.
*/
interface Type extends OldEnum<Type>, Keyed {
interface Type extends RegistryElement<Type>, OldEnum<Type>, Keyed {
// Start generate - VillagerType
Type DESERT = getType("desert");
@@ -218,7 +219,7 @@ public interface Villager extends AbstractVillager {
* Represents the various different Villager professions there may be.
* Villagers have different trading options depending on their profession,
*/
interface Profession extends OldEnum<Profession>, Keyed, net.kyori.adventure.translation.Translatable {
interface Profession extends RegistryElement<Profession>, OldEnum<Profession>, Keyed, net.kyori.adventure.translation.Translatable {
// Start generate - VillagerProfession
/**
@@ -1,7 +1,9 @@
package org.bukkit.entity;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.WolfVariantRegistryEntry;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.DyeColor;
@@ -108,7 +110,7 @@ public interface Wolf extends Tameable, Sittable, io.papermc.paper.entity.Collar
/**
* Represents the variant of a wolf.
*/
interface Variant extends Keyed {
interface Variant extends RegistryElement.Buildable<Variant, WolfVariantRegistryEntry, WolfVariantRegistryEntry.Builder>, Keyed {
// Start generate - WolfVariant
// @GeneratedFrom 1.21.5
@@ -140,7 +142,7 @@ public interface Wolf extends Tameable, Sittable, io.papermc.paper.entity.Collar
/**
* Represents the sound variant of a wolf.
*/
interface SoundVariant extends Keyed {
interface SoundVariant extends RegistryElement<SoundVariant>, Keyed {
// Start generate - WolfSoundVariant
SoundVariant ANGRY = getSoundVariant("angry");
@@ -1,7 +1,9 @@
package org.bukkit.entity;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.ZombieNautilusVariantRegistryEntry;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Keyed;
@@ -27,7 +29,7 @@ public interface ZombieNautilus extends AbstractNautilus {
/**
* Represents the variant of a Zombie Nautilus.
*/
interface Variant extends Keyed {
interface Variant extends RegistryElement.Buildable<Variant, ZombieNautilusVariantRegistryEntry, ZombieNautilusVariantRegistryEntry.Builder>, Keyed {
// Start generate - ZombieNautilusVariant
Variant TEMPERATE = getVariant("temperate");
@@ -30,7 +30,8 @@ public enum ClickType {
*/
WINDOW_BORDER_RIGHT,
/**
* The middle mouse button, or a "scrollwheel click".
* The middle click action, bound to the middle mouse button by default.
* This may also be triggered by another key if rebound by the client.
*/
MIDDLE,
/**
@@ -1,6 +1,7 @@
package org.bukkit.generator.structure;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryElement;
import io.papermc.paper.registry.RegistryKey;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
@@ -16,7 +17,7 @@ import org.jetbrains.annotations.NotNull;
* there might be additional structures present (for example structures added by
* data packs), which can be received via {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} and {@link io.papermc.paper.registry.RegistryKey#STRUCTURE}.
*/
public abstract class Structure implements Keyed {
public abstract class Structure implements RegistryElement<Structure>, Keyed {
// Start generate - Structure
public static final Structure ABANDONED_CAMP_BAMBOO_JUNGLE = getStructure("abandoned_camp_bamboo_jungle");
@@ -1,5 +1,6 @@
package org.bukkit.generator.structure;
import io.papermc.paper.registry.RegistryElement;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import org.bukkit.Keyed;
@@ -14,7 +15,7 @@ import org.jetbrains.annotations.NotNull;
* structure types added by data packs), which can be received via
* {@link Registry#STRUCTURE_TYPE}.
*/
public abstract class StructureType implements Keyed {
public abstract class StructureType implements RegistryElement<StructureType>, Keyed {
// Start generate - StructureType
public static final StructureType BURIED_TREASURE = getStructureType("buried_treasure");
@@ -1169,7 +1169,6 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
}
// Paper end - expose itemstack tooltip lines
// Paper start - data component API
/**
* Gets the value for the data component type on this stack.
*
@@ -1358,7 +1357,31 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat
* @return {@code true} if the provided item is equal, ignoring the provided components
*/
public boolean matchesWithoutData(final @NotNull ItemStack item, final @NotNull java.util.Set<io.papermc.paper.datacomponent.@NotNull DataComponentType> excludeTypes, final boolean ignoreCount) {
return this.matchesWithoutData(item, excludeTypes::contains, ignoreCount);
}
/**
* Checks if this itemstack matches another given itemstack excluding the provided components.
* This is useful if you are wanting to ignore certain properties of itemstacks, such as durability.
*
* @param item the item to compare
* @param excludeTypes the data component types to ignore
* @return {@code true} if the provided item is equal, ignoring the provided components
*/
public boolean matchesWithoutData(final @NotNull ItemStack item, final @NotNull Predicate<io.papermc.paper.datacomponent.@NotNull DataComponentType> excludeTypes) {
return this.matchesWithoutData(item, excludeTypes, false);
}
/**
* Checks if this itemstack matches another given itemstack excluding the provided components.
* This is useful if you are wanting to ignore certain properties of itemstacks, such as durability.
*
* @param item the item to compare
* @param excludeTypes the data component types to ignore
* @param ignoreCount ignore the count of the item
* @return {@code true} if the provided item is equal, ignoring the provided components
*/
public boolean matchesWithoutData(final @NotNull ItemStack item, final @NotNull Predicate<io.papermc.paper.datacomponent.@NotNull DataComponentType> excludeTypes, final boolean ignoreCount) {
return this.craftDelegate.matchesWithoutData(item, excludeTypes, ignoreCount);
}
// Paper end - data component API
}
@@ -2,6 +2,7 @@ package org.bukkit.inventory;
import com.google.common.collect.Multimap;
import io.papermc.paper.datacomponent.DataComponentType;
import io.papermc.paper.registry.RegistryElement;
import java.util.Set;
import java.util.function.Consumer;
import net.kyori.adventure.key.Key;
@@ -47,7 +48,7 @@ import org.jspecify.annotations.Nullable;
* Represents an item type.
*/
@NullMarked
public interface ItemType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - add Translatable & feature flag API
public interface ItemType extends RegistryElement<ItemType>, 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 ItemType}s that have a known item meta type
@@ -70,7 +70,7 @@ final class ItemTypeRecipeChoiceImpl extends RecipeChoice.MaterialChoice impleme
if (this.legacyChoices == null) {
final List<Material> choices = new ArrayList<>();
for (final TypedKey<ItemType> itemTypeKey : this.itemTypes) {
choices.add(Registry.MATERIAL.getOrThrow(itemTypeKey));
choices.add(Registry.MATERIAL.getOrThrow(itemTypeKey.key()));
}
this.legacyChoices = Collections.unmodifiableList(choices);
}
@@ -1,5 +1,6 @@
package org.bukkit.inventory;
import io.papermc.paper.registry.RegistryElement;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
import net.kyori.adventure.text.Component;
@@ -27,7 +28,7 @@ import org.jspecify.annotations.Nullable;
* created and viewed by the player.
*/
@NullMarked
public interface MenuType extends Keyed, io.papermc.paper.world.flag.FeatureDependant { // Paper - make FeatureDependant
public interface MenuType extends RegistryElement<MenuType>, Keyed, io.papermc.paper.world.flag.FeatureDependant { // Paper - make FeatureDependant
/**
* A MenuType which represents a chest with 1 row.
@@ -59,7 +59,7 @@ public interface RecipeChoice extends Predicate<ItemStack>, Cloneable {
/**
* Creates a new recipe choice based on a {@link RegistryKeySet} of item types.
* Can either be created via {@link RegistryKeySet#keySet(RegistryKey, TypedKey[])}
* Can either be created via {@link RegistryKeySet#keySet(RegistryKey, TypedKey[])}/{@link RegistryKeySet#keySet(TypedKey, TypedKey[])}
* or obtained from {@link org.bukkit.Registry#getTag(TagKey)}.
*
* @param itemTypes the item types to match
@@ -2,8 +2,9 @@ package org.bukkit.inventory.meta.trim;
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.TrimMaterialRegistryEntry;
import java.util.function.Consumer;
import net.kyori.adventure.key.Key;
@@ -19,7 +20,7 @@ import org.jspecify.annotations.NullMarked;
* Represents a material that may be used in an {@link ArmorTrim}.
*/
@NullMarked
public interface TrimMaterial extends Keyed, Translatable {
public interface TrimMaterial extends RegistryElement.Inlineable<TrimMaterial, TrimMaterialRegistryEntry, TrimMaterialRegistryEntry.Builder>, Keyed, Translatable {
/**
* Creates an inlined trim material.
@@ -28,7 +29,7 @@ public interface TrimMaterial extends Keyed, Translatable {
* @return the created trim material
*/
static TrimMaterial create(final Consumer<RegistryBuilderFactory<TrimMaterial, ? extends TrimMaterialRegistryEntry.Builder>> value) {
return InlinedRegistryBuilderProvider.instance().createTrimMaterial(value);
return RegistryBuilderProvider.instance().create(RegistryKey.TRIM_MATERIAL, value);
}
// Start generate - TrimMaterial
@@ -2,8 +2,9 @@ package org.bukkit.inventory.meta.trim;
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.TrimPatternRegistryEntry;
import java.util.function.Consumer;
import net.kyori.adventure.key.Key;
@@ -19,7 +20,7 @@ import org.jspecify.annotations.NullMarked;
* Represents a pattern that may be used in an {@link ArmorTrim}.
*/
@NullMarked
public interface TrimPattern extends Keyed, Translatable {
public interface TrimPattern extends RegistryElement.Inlineable<TrimPattern, TrimPatternRegistryEntry, TrimPatternRegistryEntry.Builder>, Keyed, Translatable {
/**
* Creates an inlined trim pattern.
@@ -28,7 +29,7 @@ public interface TrimPattern extends Keyed, Translatable {
* @return the created trim pattern
*/
static TrimPattern create(final Consumer<RegistryBuilderFactory<TrimPattern, ? extends TrimPatternRegistryEntry.Builder>> value) {
return InlinedRegistryBuilderProvider.instance().createTrimPattern(value);
return RegistryBuilderProvider.instance().create(RegistryKey.TRIM_PATTERN, value);
}
// Start generate - TrimPattern
@@ -1,6 +1,7 @@
package org.bukkit.map;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryElement;
import java.util.Locale;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
@@ -287,7 +288,7 @@ public final class MapCursor {
* index in the file './assets/minecraft/textures/map/map_icons.png' from minecraft.jar or from a
* resource pack.
*/
public interface Type extends OldEnum<Type>, Keyed {
public interface Type extends RegistryElement<Type>, OldEnum<Type>, Keyed {
// Start generate - MapCursorType
Type ABANDONED_CAMP = getType("abandoned_camp");
@@ -3,6 +3,7 @@ package org.bukkit.potion;
import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import io.papermc.paper.registry.RegistryElement;
import java.util.Locale;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.key.KeyPattern;
@@ -20,7 +21,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents a type of potion and its effect on an entity.
*/
public abstract class PotionEffectType implements Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - implement Translatable & feature flag API
public abstract class PotionEffectType implements RegistryElement<PotionEffectType>, Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - implement Translatable & feature flag API
private static final BiMap<Integer, PotionEffectType> ID_MAP = HashBiMap.create();
/**
+1 -1
View File
@@ -6,7 +6,7 @@ plugins {
val testData = sourceSets.create("testData")
dependencies {
implementation("com.puppycrawl.tools:checkstyle:13.8.0")
implementation("com.puppycrawl.tools:checkstyle:14.3.0")
implementation("org.jspecify:jspecify:1.0.0")
testCompileOnly("org.jetbrains:annotations:26.1.0")
@@ -15,7 +15,6 @@ import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Predicate;
import org.apache.commons.lang3.tuple.Pair;
import org.jspecify.annotations.Nullable;
import static java.util.Objects.requireNonNull;
@@ -163,7 +162,16 @@ public final class Util {
return false;
}
public static Pair<String, DetailAST> extractFullIdent(final DetailAST lastDot) {
/**
* A dotted name and the first identifier node it starts at.
*
* @param name the full dotted name
* @param startNode the first identifier node of the name
*/
public record QualifiedName(String name, DetailAST startNode) {
}
public static QualifiedName extractFullIdent(final DetailAST lastDot) {
final List<String> parts = new ArrayList<>();
DetailAST dot = lastDot;
while (dot.getChildCount(TokenTypes.DOT) > 0) {
@@ -180,6 +188,6 @@ public final class Util {
}
parts.addFirst(ident.getText());
return Pair.of(String.join(".", parts), ident);
return new QualifiedName(String.join(".", parts), ident);
}
}
@@ -6,7 +6,9 @@ import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
import io.papermc.checkstyle.Util;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -19,7 +21,7 @@ public final class JavadocAlignParameterDescriptionCheck extends AbstractJavadoc
@Override
public int[] getDefaultJavadocTokens() {
return new int[]{JavadocCommentsTokenTypes.JAVADOC_CONTENT};
return new int[] {JavadocCommentsTokenTypes.JAVADOC_CONTENT};
}
@Override
@@ -30,6 +32,7 @@ public final class JavadocAlignParameterDescriptionCheck extends AbstractJavadoc
final List<ParamDesc> params = new ArrayList<>();
int maxColumn = -1;
DetailNode child = detailNode.getFirstChild();
final Set<Integer> visitedLines = new HashSet<>();
for (final DetailNode javadocBlock : JavadocUtil.getAllNodesOfType(detailNode, JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG)) {
final DetailNode paramBlock = JavadocUtil.findFirstToken(javadocBlock, JavadocCommentsTokenTypes.PARAM_BLOCK_TAG);
if (paramBlock == null) {
@@ -41,6 +44,9 @@ public final class JavadocAlignParameterDescriptionCheck extends AbstractJavadoc
}
// iterate over all text nodes (multiline)
for (final DetailNode textNode : JavadocUtil.getAllNodesOfType(paramDescription, JavadocCommentsTokenTypes.TEXT)) {
if (!visitedLines.add(textNode.getLineNumber())) {
continue;
}
final Matcher matcher = SPACE_PREFIX.matcher(textNode.getText());
int paramDescColNum = textNode.getColumnNumber();
if (matcher.find()) {
@@ -0,0 +1,123 @@
package io.papermc.checkstyle.checks;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.DetailNode;
import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
import com.puppycrawl.tools.checkstyle.utils.BlockCommentPosition;
import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
import io.papermc.checkstyle.Util;
import java.util.ArrayList;
import java.util.List;
import org.jspecify.annotations.Nullable;
/**
* Checks that {@code @param} tags are in the same order as the parameters they document,
* with type parameters after regular parameters (the order IntelliJ generates them in).
* Only tags that move backward in the expected order are reported, unknown names are ignored.
*/
public final class JavadocParamTagOrderCheck extends AbstractJavadocCheck {
private static final int[] DECLARATIONS = {
TokenTypes.METHOD_DEF,
TokenTypes.CTOR_DEF,
TokenTypes.COMPACT_CTOR_DEF,
TokenTypes.CLASS_DEF,
TokenTypes.INTERFACE_DEF,
TokenTypes.RECORD_DEF,
};
@Override
public int[] getDefaultJavadocTokens() {
return new int[] {JavadocCommentsTokenTypes.JAVADOC_CONTENT};
}
@Override
public void visitJavadocToken(final DetailNode detailNode) {
final DetailAST declaration = documentedDeclaration(this.getBlockCommentAst());
if (declaration == null) {
return;
}
final List<String> expectedOrder = expectedParamOrder(declaration);
int furthestIndex = -1;
for (final DetailNode javadocBlock : JavadocUtil.getAllNodesOfType(detailNode, JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG)) {
final DetailNode paramBlock = JavadocUtil.findFirstToken(javadocBlock, JavadocCommentsTokenTypes.PARAM_BLOCK_TAG);
if (paramBlock == null) {
continue;
}
final DetailNode paramName = JavadocUtil.findFirstToken(paramBlock, JavadocCommentsTokenTypes.PARAMETER_NAME);
if (paramName == null) {
continue;
}
final int index = expectedOrder.indexOf(paramName.getText());
if (index == -1) {
continue;
}
if (index < furthestIndex) {
this.log(
paramName.getLineNumber(),
paramName.getColumnNumber(),
"@param tag for %s should be in declaration order, with type parameters last".formatted(paramName.getText())
);
} else {
furthestIndex = index;
}
}
}
private static @Nullable DetailAST documentedDeclaration(final DetailAST blockComment) {
if (!BlockCommentPosition.isOnMethod(blockComment)
&& !BlockCommentPosition.isOnConstructor(blockComment)
&& !BlockCommentPosition.isOnCompactConstructor(blockComment)
&& !BlockCommentPosition.isOnClass(blockComment)
&& !BlockCommentPosition.isOnInterface(blockComment)
&& !BlockCommentPosition.isOnRecord(blockComment)) {
return null;
}
DetailAST parent = blockComment.getParent();
while (parent != null && !TokenUtil.isOfType(parent, DECLARATIONS)) {
parent = parent.getParent();
}
return parent;
}
private static List<String> expectedParamOrder(final DetailAST declaration) {
final List<String> names = new ArrayList<>();
if (declaration.getType() == TokenTypes.COMPACT_CTOR_DEF) {
// compact constructors document the components of the enclosing record
addRecordComponentNames(names, declaration.getParent().getParent());
return names;
}
final DetailAST parameters = declaration.findFirstToken(TokenTypes.PARAMETERS);
if (parameters != null) {
for (final DetailAST parameter : Util.childrenIterator(parameters, TokenTypes.PARAMETER_DEF)) {
names.add(identText(parameter));
}
}
addRecordComponentNames(names, declaration);
final DetailAST typeParameters = declaration.findFirstToken(TokenTypes.TYPE_PARAMETERS);
if (typeParameters != null) {
for (final DetailAST typeParameter : Util.childrenIterator(typeParameters, TokenTypes.TYPE_PARAMETER)) {
names.add("<" + identText(typeParameter) + ">");
}
}
return names;
}
private static void addRecordComponentNames(final List<String> names, final DetailAST declaration) {
final DetailAST recordComponents = declaration.findFirstToken(TokenTypes.RECORD_COMPONENTS);
if (recordComponents == null) {
return;
}
for (final DetailAST component : Util.childrenIterator(recordComponents, TokenTypes.RECORD_COMPONENT_DEF)) {
names.add(identText(component));
}
}
private static String identText(final DetailAST ast) {
final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
return ident == null ? "" : ident.getText();
}
}
@@ -28,7 +28,7 @@ public final class NullabilityAnnotationsCheck extends AbstractCheck {
@Override
public int[] getRequiredTokens() {
return new int[]{
return new int[] {
TokenTypes.METHOD_DEF,
TokenTypes.PARAMETER_DEF,
TokenTypes.ANNOTATION_FIELD_DEF,
@@ -20,7 +20,7 @@ public class RedundantNullabilityCheck extends AbstractCheck {
@Override
public int[] getRequiredTokens() {
return new int[]{
return new int[] {
TokenTypes.CLASS_DEF,
TokenTypes.INTERFACE_DEF,
TokenTypes.ANNOTATION_DEF,
@@ -31,10 +31,20 @@ public class RedundantNullabilityCheck extends AbstractCheck {
@Override
public void visitToken(final DetailAST ast) {
final boolean pkgIsNullMarked = Util.isPackageInfoAnnotated(Path.of(this.getFilePath()), NullabilityAnnotationsCheck::isNullMarkedAnnotation);
final DetailAST nullMarkedAnnotation = NullabilityAnnotationsCheck.getNullMarkedAnnotation(ast);
if (pkgIsNullMarked && nullMarkedAnnotation != null) {
if (nullMarkedAnnotation == null) {
return;
}
final boolean pkgIsNullMarked = Util.isPackageInfoAnnotated(Path.of(this.getFilePath()), NullabilityAnnotationsCheck::isNullMarkedAnnotation);
if (pkgIsNullMarked) {
this.log(nullMarkedAnnotation.getLineNo(), ast.getColumnNo() - 1, "Redundant NullMarked annotation");
return;
}
for (DetailAST parentDef = Util.getEnclosingTypeDeclaration(ast); parentDef != null; parentDef = Util.getEnclosingTypeDeclaration(parentDef)) {
if (NullabilityAnnotationsCheck.getNullMarkedAnnotation(parentDef) != null) {
this.log(nullMarkedAnnotation.getLineNo(), ast.getColumnNo() - 1, "Redundant NullMarked annotation");
return;
}
}
}
}
@@ -1,13 +1,26 @@
package io.papermc.checkstyle.checks;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.FullIdent;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
import io.papermc.checkstyle.Util;
import io.papermc.checkstyle.checks.util.ImportAwareAbstractCheck;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.jspecify.annotations.Nullable;
/**
* Reports fully qualified type references that could be imported instead.
* <p>
* A reference is only reported if its simple name doesn't already refer to another type in the file,
* either through an import, a type declared in the file, or an unqualified use of that name.
* <p>
* Requires that the codebase be set up to follow standard naming conventions.
* <ul>
* <li>packages are always all lowercase</li>
@@ -16,6 +29,35 @@ import org.jspecify.annotations.Nullable;
*/
public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractCheck {
private static final String JAVA_LANG = "java.lang";
private static final int[] TYPE_CONTEXTS = {
TokenTypes.TYPE,
TokenTypes.TYPE_ARGUMENT,
TokenTypes.TYPE_PARAMETER,
TokenTypes.TYPE_UPPER_BOUNDS,
TokenTypes.TYPE_LOWER_BOUNDS,
TokenTypes.EXTENDS_CLAUSE,
TokenTypes.PERMITS_CLAUSE,
TokenTypes.IMPLEMENTS_CLAUSE,
TokenTypes.LITERAL_THROWS,
TokenTypes.LITERAL_NEW,
TokenTypes.ANNOTATION,
};
private final ThreadLocal<@Nullable FileScope> fileScope = new ThreadLocal<>();
/**
* Type names visible in the file being checked, other than imports.
*
* @param packageName the package of the file
* @param topLevelTypes simple names of top level types declared in the file
* @param nestedTypes simple names of nested and local types declared in the file
* @param unqualifiedTypes simple names of types referenced without qualification
*/
private record FileScope(String packageName, Set<String> topLevelTypes, Set<String> nestedTypes, Set<String> unqualifiedTypes) {
}
@Override
public int[] getDefaultTokens() {
return this.getRequiredTokens();
@@ -28,7 +70,7 @@ public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractChe
@Override
public int[] getRequiredTokens() {
return new int[]{
return new int[] {
TokenTypes.TYPE,
TokenTypes.TYPE_ARGUMENT,
TokenTypes.TYPE_PARAMETER,
@@ -37,55 +79,168 @@ public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractChe
TokenTypes.EXTENDS_CLAUSE,
TokenTypes.PERMITS_CLAUSE,
TokenTypes.IMPLEMENTS_CLAUSE,
TokenTypes.LITERAL_THROWS,
TokenTypes.LITERAL_NEW,
TokenTypes.ANNOTATION,
TokenTypes.METHOD_CALL,
TokenTypes.METHOD_REF,
TokenTypes.LITERAL_CLASS,
TokenTypes.DOT,
};
}
private static @Nullable DetailAST fromMethodCall(final DetailAST methodCall) {
final DetailAST dot = methodCall.findFirstToken(TokenTypes.DOT);
if (dot == null) {
return null;
@Override
public void beginTree(final DetailAST rootAST) {
super.beginTree(rootAST);
String packageName = "";
final Set<String> topLevelTypes = new HashSet<>();
final Set<String> nestedTypes = new HashSet<>();
final Set<String> unqualifiedTypes = new HashSet<>();
final Deque<DetailAST> nodes = new ArrayDeque<>();
for (DetailAST node = rootAST; node != null; node = node.getNextSibling()) {
nodes.push(node);
}
final DetailAST nextDot = dot.findFirstToken(TokenTypes.DOT);
if (nextDot == null) {
return null;
while (!nodes.isEmpty()) {
final DetailAST node = nodes.pop();
for (DetailAST child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
nodes.push(child);
}
if (node.getType() == TokenTypes.PACKAGE_DEF) {
packageName = FullIdent.createFullIdent(node.getLastChild().getPreviousSibling()).getText();
} else if (node.getType() == TokenTypes.IDENT) {
final DetailAST parent = node.getParent();
if (TokenUtil.isTypeDeclaration(parent.getType())) {
final DetailAST declarationParent = parent.getParent();
if (declarationParent == null || declarationParent.getType() == TokenTypes.COMPILATION_UNIT) {
topLevelTypes.add(node.getText());
} else {
nestedTypes.add(node.getText());
}
} else if (isUnqualifiedTypeReference(node, parent)) {
unqualifiedTypes.add(node.getText());
}
}
}
final DetailAST ident = nextDot.findFirstToken(TokenTypes.IDENT);
if (ident == null || Character.isLowerCase(ident.getText().charAt(0))) {
// method call not on a type
return null;
}
return dot;
this.fileScope.set(new FileScope(packageName, topLevelTypes, nestedTypes, unqualifiedTypes));
}
private static @Nullable DetailAST fromLiteralClass(final DetailAST literalClass) {
final DetailAST dot = literalClass.getParent();
if (dot.getType() != TokenTypes.DOT) {
return null;
@Override
public void finishTree(final DetailAST rootAST) {
super.finishTree(rootAST);
this.fileScope.remove();
}
private static boolean isUnqualifiedTypeReference(final DetailAST ident, final DetailAST parent) {
if (TokenUtil.isOfType(parent, TYPE_CONTEXTS)) {
return true;
}
return dot;
// first part of a dotted name or method reference, like Type.member or Type::member
return Character.isUpperCase(ident.getText().charAt(0))
&& TokenUtil.isOfType(parent, TokenTypes.DOT, TokenTypes.METHOD_REF)
&& parent.getFirstChild() == ident;
}
@Override
public void visitToken(final DetailAST ast) {
final DetailAST actualAst = switch (ast.getType()) {
case TokenTypes.METHOD_CALL -> fromMethodCall(ast);
case TokenTypes.LITERAL_CLASS -> fromLiteralClass(ast);
default -> ast;
};
if (actualAst == null) {
switch (ast.getType()) {
case TokenTypes.METHOD_CALL -> {
final DetailAST callee = ast.findFirstToken(TokenTypes.DOT);
if (callee != null) {
this.checkExpressionChain(callee, true);
}
}
case TokenTypes.METHOD_REF -> this.checkExpressionChain(ast.getFirstChild(), false);
case TokenTypes.DOT -> {
if (isFieldAccess(ast)) {
this.checkExpressionChain(ast, true);
}
}
case TokenTypes.LITERAL_CLASS -> {
final DetailAST dot = ast.getParent();
if (dot.getType() == TokenTypes.DOT) {
this.checkChildren(dot);
}
}
default -> this.checkChildren(ast);
}
}
private void checkChildren(final DetailAST ast) {
// clauses like extends, implements, throws, and bounds can contain multiple types
for (final DetailAST dot : Util.childrenIterator(ast, TokenTypes.DOT)) {
this.checkQualifiedName(dot);
}
}
/**
* Checks the type qualifier in a dotted expression, like {@code java.util.Locale} in {@code java.util.Locale.ROOT}.
*
* @param chain the outermost node of the dotted expression
* @param lastIsMember whether the last part of the chain is a member (field or method) and not part of the type
*/
private void checkExpressionChain(final DetailAST chain, final boolean lastIsMember) {
final List<DetailAST> dots = new ArrayList<>();
DetailAST node = chain;
while (node.getType() == TokenTypes.DOT) {
dots.addFirst(node);
node = node.getFirstChild();
}
if (node.getType() != TokenTypes.IDENT) {
// starts with this, super, a method call, etc.
return;
}
final DetailAST dot = actualAst.findFirstToken(TokenTypes.DOT);
if (dot == null) {
final List<String> parts = new ArrayList<>();
parts.add(node.getText());
for (final DetailAST dot : dots) {
final DetailAST name = Util.getNextSibling(dot.getFirstChild(), TokenTypes.IDENT);
if (name == null) {
// .class, .this, .new, etc.
return;
}
parts.add(name.getText());
}
int packageEnd = 0;
while (packageEnd < parts.size() && !Character.isUpperCase(parts.get(packageEnd).charAt(0))) {
packageEnd++;
}
if (packageEnd == 0 || packageEnd == parts.size()) {
// not qualified by a package, or no type at all
return;
}
final Pair<String, DetailAST> fullIdent = Util.extractFullIdent(dot);
final DetailAST startNode = fullIdent.getRight();
final String fullIdentName = fullIdent.getLeft();
int typeEnd = packageEnd;
while (typeEnd + 1 < parts.size() && Character.isUpperCase(parts.get(typeEnd + 1).charAt(0))) {
typeEnd++;
}
if (lastIsMember && typeEnd == parts.size() - 1) {
typeEnd--;
}
if (typeEnd < packageEnd) {
// only a member, like local.inner.FIELD
return;
}
this.checkQualifiedName(dots.get(typeEnd - 1));
}
private static boolean isFieldAccess(final DetailAST dot) {
final DetailAST parent = dot.getParent();
if (TokenUtil.isOfType(parent, TokenTypes.DOT, TokenTypes.METHOD_CALL, TokenTypes.METHOD_REF) || TokenUtil.isOfType(parent, TYPE_CONTEXTS)) {
return false;
}
for (DetailAST ancestor = parent; ancestor != null; ancestor = ancestor.getParent()) {
if (ancestor.getType() == TokenTypes.EXPR) {
return true;
}
if (TokenUtil.isOfType(ancestor, TYPE_CONTEXTS)) {
return false;
}
}
return false;
}
private void checkQualifiedName(final DetailAST dot) {
final Util.QualifiedName fullIdent = Util.extractFullIdent(dot);
final DetailAST startNode = fullIdent.startNode();
final String fullIdentName = fullIdent.name();
if (Character.isUpperCase(fullIdentName.charAt(0))) {
// not a package
return;
@@ -96,7 +251,7 @@ public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractChe
do {
// Check if we have an import for this exact type
if (this.hasImportFor(typeName)) {
this.log(startNode.getLineNo(), startNode.getColumnNo(), "Fully qualified import used: " + fullIdentName);
this.log(startNode.getLineNo(), startNode.getColumnNo(), "Fully qualified import used: " + withoutConstants(fullIdentName));
return;
}
previousTypeName = typeName;
@@ -104,9 +259,61 @@ public class UnnecessaryFullyQualifiedImportCheck extends ImportAwareAbstractChe
} while ((lastDotIdx = typeName.lastIndexOf('.')) != -1 && Character.isUpperCase(typeName.substring(0, lastDotIdx + 2).charAt(lastDotIdx + 1)));
final String outerSimpleName = previousTypeName.substring(previousTypeName.lastIndexOf('.') + 1);
final String fullImportBySimple = this.getFullImportBySimple(outerSimpleName);
if (fullImportBySimple == null) {
this.log(startNode.getLineNo(), startNode.getColumnNo(), "Unnecessary fully qualified import (no conflict): " + fullIdentName);
final String outerPackage = previousTypeName.substring(0, previousTypeName.lastIndexOf('.'));
if (this.getFullImportBySimple(outerSimpleName) == null && !this.isNameTaken(outerSimpleName, outerPackage)) {
this.log(startNode.getLineNo(), startNode.getColumnNo(), "Unnecessary fully qualified import (no conflict): " + withoutConstants(fullIdentName));
}
}
/**
* Checks if the simple name already refers to a different type in this file.
*
* @param simpleName the simple name of the outermost type
* @param packageName the package of the outermost type
* @return true if the type can't be referenced by its simple name
*/
private boolean isNameTaken(final String simpleName, final String packageName) {
final FileScope scope = this.fileScope.get();
if (scope == null) {
return false;
}
final boolean samePackage = packageName.equals(scope.packageName());
if (scope.nestedTypes().contains(simpleName)) {
// a nested type shadows any other type with the same name
return true;
}
if (scope.topLevelTypes().contains(simpleName)) {
// a top level type with the same name is only the same type if it's in the same package
return !samePackage;
}
if (scope.unqualifiedTypes().contains(simpleName)) {
// unqualified names resolve to the same package or java.lang (imports are already handled)
return !samePackage && !JAVA_LANG.equals(packageName);
}
return false;
}
/**
* Removes trailing constants from a dotted name, like {@code ROOT} in {@code java.util.Locale.ROOT}.
*
* @param name the dotted name
* @return the name without trailing constants
*/
private static String withoutConstants(final String name) {
String result = name;
int lastDot;
while ((lastDot = result.lastIndexOf('.')) != -1 && isConstantName(result.substring(lastDot + 1))) {
final String qualifier = result.substring(0, lastDot);
if (!Character.isUpperCase(qualifier.charAt(qualifier.lastIndexOf('.') + 1))) {
// the constant-like part is the outermost type, like java.util.UUID
break;
}
result = qualifier;
}
return result;
}
private static boolean isConstantName(final String name) {
return name.length() > 1 && name.equals(name.toUpperCase(Locale.ROOT)) && name.chars().anyMatch(Character::isLetter);
}
}
@@ -43,6 +43,7 @@ public abstract class ImportAwareAbstractCheck extends AbstractCheck {
@Override
public void finishTree(final DetailAST rootAST) {
this.imports.remove();
this.simpleImports.remove();
}
protected boolean hasImportFor(final String type) {
@@ -12,12 +12,12 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.intellij.lang.annotations.Language;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
@@ -27,6 +27,7 @@ import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.platform.commons.support.AnnotationSupport;
import org.junit.platform.commons.support.ReflectionSupport;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -68,10 +69,11 @@ public @interface CheckTest {
assertFalse(builder.checks.isEmpty(), "CheckTestBuilder must have at least one check");
final TreeWalker walker = new TreeWalker();
walker.setModuleFactory(new PackageObjectFactory("io.papermc.checkstyle.checks", Util.class.getClassLoader()));
final Method registerCheck = ReflectionSupport.findMethod(TreeWalker.class, "registerCheck", AbstractCheck.class).orElseThrow();
for (final AbstractCheck check : builder.checks) {
check.init();
check.configure(new DefaultConfiguration("Test"));
MethodUtils.invokeMethod(walker, true, "registerCheck", check);
ReflectionSupport.invokeMethod(registerCheck, walker, check);
}
final Path filePath = Path.of("src", "testData", "java", checkTest.value().replace('.', File.separatorChar) + ".java");
assertTrue(Files.exists(filePath), "File not found: " + filePath);
@@ -0,0 +1,29 @@
package io.papermc.checkstyle.checks;
import io.papermc.checkstyle.CheckTest;
import io.papermc.checkstyle.CheckTestBuilder;
class TestJavadocParamTagOrderCheck {
@CheckTest("testdata.JavadocParamTagOrder")
void test(final CheckTestBuilder builder) {
builder
.addCheck(new JavadocParamTagOrderCheck())
// regular parameters swapped
.addViolation("28:15: @param tag for a should be in declaration order, with type parameters last")
// type parameter before regular parameter
.addViolation("37:15: @param tag for a should be in declaration order, with type parameters last")
// type parameters swapped
.addViolation("47:15: @param tag for <T> should be in declaration order, with type parameters last")
// constructor
.addViolation("66:15: @param tag for a should be in declaration order, with type parameters last")
// class type parameters
.addViolation("75:15: @param tag for <A> should be in declaration order, with type parameters last")
// record component after type parameter
.addViolation("94:15: @param tag for a should be in declaration order, with type parameters last")
// record components swapped
.addViolation("111:15: @param tag for a should be in declaration order, with type parameters last")
// compact constructor
.addViolation("119:19: @param tag for a should be in declaration order, with type parameters last");
}
}
@@ -0,0 +1,17 @@
package io.papermc.checkstyle.checks;
import io.papermc.checkstyle.CheckTest;
import io.papermc.checkstyle.CheckTestBuilder;
class TestRedundantNullabilityCheck {
@CheckTest("testdata.RedundantNullability")
void test(final CheckTestBuilder builder) {
builder
.addViolation("10:8: Redundant NullMarked annotation")
.addViolation("14:8: Redundant NullMarked annotation")
.addViolation("18:8: Redundant NullMarked annotation")
.addViolation("22:8: Redundant NullMarked annotation")
.addCheck(new RedundantNullabilityCheck());
}
}
@@ -32,6 +32,32 @@ class TestUnnecessaryFullyQualifiedImportCheck {
.addViolation("66:5: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass")
// class literal ".class"
.addViolation("70:28: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass")
// throws clause, multiple types
.addViolation("74:39: Unnecessary fully qualified import (no conflict): java.io.IOException")
.addViolation("74:60: Unnecessary fully qualified import (no conflict): java.io.UncheckedIOException")
// type param with multiple bounds
.addViolation("77:36: Unnecessary fully qualified import (no conflict): testdata.imports.testclasses.subpkg.OtherInterface")
.addViolation("77:89: Unnecessary fully qualified import (no conflict): java.io.Serializable")
// implements clause, multiple types
.addViolation("80:57: Unnecessary fully qualified import (no conflict): testdata.imports.testclasses.subpkg.OtherInterface")
.addViolation("80:109: Unnecessary fully qualified import (no conflict): java.io.Serializable")
// static field access
.addViolation("84:36: Unnecessary fully qualified import (no conflict): java.util.Locale")
.addViolation("85:35: Fully qualified import used: testdata.imports.testclasses.subpkg.OtherClass")
// method call through a field
.addViolation("86:9: Unnecessary fully qualified import (no conflict): java.lang.System")
// method call on a constant, reported without the constant
.addViolation("87:29: Unnecessary fully qualified import (no conflict): java.util.Locale")
// method and constructor references
.addViolation("88:36: Unnecessary fully qualified import (no conflict): java.lang.Thread")
.addViolation("89:41: Unnecessary fully qualified import (no conflict): java.util.ArrayList")
// no violation for holder.inner.VALUE (91), java.util.Random conflicting with a declared type (103),
// or testdata.imports.testclasses.SamePackage conflicting with an unqualified use (108)
// reference to the declared type itself
.addViolation("104:5: Unnecessary fully qualified import (no conflict): testdata.imports.UnnecessaryFullyQualifiedImport")
// same package type, no conflict
.addViolation("109:5: Unnecessary fully qualified import (no conflict): testdata.imports.SamePackage")
// no violation for testdata.imports.Shadowed (115), the nested Shadowed hides the same package type
.addCheck(new UnnecessaryFullyQualifiedImportCheck());
}
}
Loaded 100 of 284 files, more files were not shown because too many files have changed in this diff. Show more