diff --git a/.editorconfig b/.editorconfig index 9c1217210..65898b518 100644 --- a/.editorconfig +++ b/.editorconfig @@ -31,6 +31,11 @@ ij_java_generate_final_locals = true ij_java_generate_final_parameters = true ij_java_method_parameters_new_line_after_left_paren = true ij_java_method_parameters_right_paren_on_new_line = true +ij_java_use_fq_class_names = false +ij_java_class_names_in_javadoc = 1 + +[paper-server/src/minecraft/java/**/*.java] +ij_java_use_fq_class_names = true [paper-server/src/minecraft/resources/data/**/*.json] indent_size = 2 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ce9d2caae..6449e1ba2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,8 +16,17 @@ on: jobs: build: - # Run on all label events (won't be duplicated) or all push events or on PR syncs not from the same repo - if: (github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'build-pr-jar') || github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name + # The goal of the build workflow is split into multiple requirements. + # 1. Run on pushes to same repo. + # 2. Run on PR open/reopen/syncs from repos that are not the same (PRs from the same repo are covered by 1) + # 3. Run on labeled PRs that have the build-pr-jar flag. + if: > + ( + (github.event_name == 'push') + || (github.event_name == 'pull_request' && github.repository != github.event.pull_request.head.repo.full_name && contains(fromJSON('["opened", "reopened", "synchronize"]'), github.event.action)) + || (github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'build-pr-jar') + ) + runs-on: ubuntu-latest strategy: matrix: diff --git a/build-data/mappings-patch.tiny b/build-data/mappings-patch.tiny index 14a2a3582..7c104e368 100644 --- a/build-data/mappings-patch.tiny +++ b/build-data/mappings-patch.tiny @@ -1,4 +1,4 @@ -tiny 2 0 spigot mojang+yarn +tiny 2 0 spigot mojang # Originally DistanceManager, which also implements DistanceManager, so clashes since the implemented class # is imported and not fully qualified. Easiest fix is to just change the name diff --git a/build-data/paper.at b/build-data/paper.at index 8e1b527ed..47ca2bb35 100644 --- a/build-data/paper.at +++ b/build-data/paper.at @@ -110,6 +110,8 @@ public net.minecraft.server.network.ServerLoginPacketListenerImpl connection public net.minecraft.server.network.ServerLoginPacketListenerImpl state public net.minecraft.server.network.ServerLoginPacketListenerImpl$State public net.minecraft.server.packs.VanillaPackResourcesBuilder safeGetPath(Ljava/net/URI;)Ljava/nio/file/Path; +public net.minecraft.server.packs.repository.FolderRepositorySource$FolderPackDetector +public net.minecraft.server.packs.repository.FolderRepositorySource$FolderPackDetector (Lnet/minecraft/world/level/validation/DirectoryValidator;)V public net.minecraft.server.packs.repository.Pack resources public net.minecraft.server.players.PlayerList playerIo public net.minecraft.server.players.PlayerList players diff --git a/build-data/reobf-mappings-patch.tiny b/build-data/reobf-mappings-patch.tiny index b14d9ed34..55a1c0eab 100644 --- a/build-data/reobf-mappings-patch.tiny +++ b/build-data/reobf-mappings-patch.tiny @@ -10,7 +10,7 @@ # a significant amount of time for us to track down every possible issue, so this file will likely be around and in # use - at least in some capacity - for a long time. -tiny 2 0 mojang+yarn spigot +tiny 2 0 mojang spigot # CraftBukkit changes type c net/minecraft/server/level/ServerLevel net/minecraft/server/level/WorldServer diff --git a/build.gradle.kts b/build.gradle.kts index b4bff6143..b4efe371f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ import java.nio.file.Path import kotlin.random.Random plugins { - id("io.papermc.paperweight.core") version "2.0.0-beta.8" apply false + id("io.papermc.paperweight.core") version "2.0.0-beta.10" apply false } subprojects { diff --git a/paper-api-generator/build.gradle.kts b/paper-api-generator/build.gradle.kts index 67d1ae285..57d1018a9 100644 --- a/paper-api-generator/build.gradle.kts +++ b/paper-api-generator/build.gradle.kts @@ -16,7 +16,7 @@ dependencies { implementation("com.squareup:javapoet:1.13.0") implementation(project(":paper-api")) implementation("io.github.classgraph:classgraph:4.8.47") - implementation("org.jetbrains:annotations:24.1.0") + implementation("org.jetbrains:annotations:26.0.1") testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } diff --git a/paper-api-generator/generated/io/papermc/paper/registry/keys/tags/EntityTypeTagKeys.java b/paper-api-generator/generated/io/papermc/paper/registry/keys/tags/EntityTypeTagKeys.java new file mode 100644 index 000000000..5a671ff8b --- /dev/null +++ b/paper-api-generator/generated/io/papermc/paper/registry/keys/tags/EntityTypeTagKeys.java @@ -0,0 +1,288 @@ +package io.papermc.paper.registry.keys.tags; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.generated.GeneratedFrom; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.tag.TagKey; +import net.kyori.adventure.key.Key; +import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#ENTITY_TYPE}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@GeneratedFrom("1.21.4") +@NullMarked +@ApiStatus.Experimental +public final class EntityTypeTagKeys { + /** + * {@code #minecraft:aquatic} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey AQUATIC = create(key("aquatic")); + + /** + * {@code #minecraft:arrows} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey ARROWS = create(key("arrows")); + + /** + * {@code #minecraft:arthropod} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey ARTHROPOD = create(key("arthropod")); + + /** + * {@code #minecraft:axolotl_always_hostiles} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey AXOLOTL_ALWAYS_HOSTILES = create(key("axolotl_always_hostiles")); + + /** + * {@code #minecraft:axolotl_hunt_targets} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey AXOLOTL_HUNT_TARGETS = create(key("axolotl_hunt_targets")); + + /** + * {@code #minecraft:beehive_inhabitors} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey BEEHIVE_INHABITORS = create(key("beehive_inhabitors")); + + /** + * {@code #minecraft:boat} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey BOAT = create(key("boat")); + + /** + * {@code #minecraft:can_breathe_under_water} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey CAN_BREATHE_UNDER_WATER = create(key("can_breathe_under_water")); + + /** + * {@code #minecraft:can_turn_in_boats} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey CAN_TURN_IN_BOATS = create(key("can_turn_in_boats")); + + /** + * {@code #minecraft:deflects_projectiles} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey DEFLECTS_PROJECTILES = create(key("deflects_projectiles")); + + /** + * {@code #minecraft:dismounts_underwater} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey DISMOUNTS_UNDERWATER = create(key("dismounts_underwater")); + + /** + * {@code #minecraft:fall_damage_immune} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey FALL_DAMAGE_IMMUNE = create(key("fall_damage_immune")); + + /** + * {@code #minecraft:freeze_hurts_extra_types} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey FREEZE_HURTS_EXTRA_TYPES = create(key("freeze_hurts_extra_types")); + + /** + * {@code #minecraft:freeze_immune_entity_types} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey FREEZE_IMMUNE_ENTITY_TYPES = create(key("freeze_immune_entity_types")); + + /** + * {@code #minecraft:frog_food} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey FROG_FOOD = create(key("frog_food")); + + /** + * {@code #minecraft:ignores_poison_and_regen} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey IGNORES_POISON_AND_REGEN = create(key("ignores_poison_and_regen")); + + /** + * {@code #minecraft:illager} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey ILLAGER = create(key("illager")); + + /** + * {@code #minecraft:illager_friends} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey ILLAGER_FRIENDS = create(key("illager_friends")); + + /** + * {@code #minecraft:immune_to_infested} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey IMMUNE_TO_INFESTED = create(key("immune_to_infested")); + + /** + * {@code #minecraft:immune_to_oozing} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey IMMUNE_TO_OOZING = create(key("immune_to_oozing")); + + /** + * {@code #minecraft:impact_projectiles} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey IMPACT_PROJECTILES = create(key("impact_projectiles")); + + /** + * {@code #minecraft:inverted_healing_and_harm} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey INVERTED_HEALING_AND_HARM = create(key("inverted_healing_and_harm")); + + /** + * {@code #minecraft:no_anger_from_wind_charge} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey NO_ANGER_FROM_WIND_CHARGE = create(key("no_anger_from_wind_charge")); + + /** + * {@code #minecraft:non_controlling_rider} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey NON_CONTROLLING_RIDER = create(key("non_controlling_rider")); + + /** + * {@code #minecraft:not_scary_for_pufferfish} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey NOT_SCARY_FOR_PUFFERFISH = create(key("not_scary_for_pufferfish")); + + /** + * {@code #minecraft:powder_snow_walkable_mobs} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey POWDER_SNOW_WALKABLE_MOBS = create(key("powder_snow_walkable_mobs")); + + /** + * {@code #minecraft:raiders} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey RAIDERS = create(key("raiders")); + + /** + * {@code #minecraft:redirectable_projectile} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey REDIRECTABLE_PROJECTILE = create(key("redirectable_projectile")); + + /** + * {@code #minecraft:sensitive_to_bane_of_arthropods} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SENSITIVE_TO_BANE_OF_ARTHROPODS = create(key("sensitive_to_bane_of_arthropods")); + + /** + * {@code #minecraft:sensitive_to_impaling} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SENSITIVE_TO_IMPALING = create(key("sensitive_to_impaling")); + + /** + * {@code #minecraft:sensitive_to_smite} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SENSITIVE_TO_SMITE = create(key("sensitive_to_smite")); + + /** + * {@code #minecraft:skeletons} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey SKELETONS = create(key("skeletons")); + + /** + * {@code #minecraft:undead} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey UNDEAD = create(key("undead")); + + /** + * {@code #minecraft:wither_friends} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey WITHER_FRIENDS = create(key("wither_friends")); + + /** + * {@code #minecraft:zombies} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey ZOMBIES = create(key("zombies")); + + private EntityTypeTagKeys() { + } + + /** + * Creates a tag key for {@link EntityType} in the registry {@code minecraft:entity_type}. + * + * @param key the tag key's key + * @return a new tag key + */ + @ApiStatus.Experimental + public static TagKey create(final Key key) { + return TagKey.create(RegistryKey.ENTITY_TYPE, key); + } +} diff --git a/paper-api-generator/src/main/java/io/papermc/generator/Generators.java b/paper-api-generator/src/main/java/io/papermc/generator/Generators.java index 73e4f7082..677a1d449 100644 --- a/paper-api-generator/src/main/java/io/papermc/generator/Generators.java +++ b/paper-api-generator/src/main/java/io/papermc/generator/Generators.java @@ -21,6 +21,7 @@ import org.bukkit.block.banner.PatternType; import org.bukkit.damage.DamageType; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Cat; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Frog; import org.bukkit.entity.Villager; import org.bukkit.entity.Wolf; @@ -79,6 +80,9 @@ public interface Generators { simpleTagKey("BannerPatternTagKeys", PatternType.class, Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN), simpleTagKey("PaintingVariantTagKeys", Art.class, Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT), simpleTagKey("InstrumentTagKeys", MusicInstrument.class, Registries.INSTRUMENT, RegistryKey.INSTRUMENT), + + // api only + simpleTagKey("EntityTypeTagKeys", EntityType.class, Registries.ENTITY_TYPE, RegistryKey.ENTITY_TYPE), new MobGoalGenerator("VanillaGoal", "com.destroystokyo.paper.entity.ai") }; diff --git a/paper-api/build.gradle.kts b/paper-api/build.gradle.kts index 9a03c630b..9da148e36 100644 --- a/paper-api/build.gradle.kts +++ b/paper-api/build.gradle.kts @@ -9,9 +9,9 @@ java { withJavadocJar() } -val annotationsVersion = "24.1.0" +val annotationsVersion = "26.0.1" val bungeeCordChatVersion = "1.20-R0.2" -val adventureVersion = "4.17.0" +val adventureVersion = "4.18.0" val slf4jVersion = "2.0.9" val log4jVersion = "2.17.1" @@ -68,9 +68,6 @@ dependencies { apiAndDocs("net.kyori:adventure-text-serializer-plain") apiAndDocs("net.kyori:adventure-text-logger-slf4j") - implementation("org.ow2.asm:asm:9.7.1") - implementation("org.ow2.asm:asm-commons:9.7.1") - api("org.apache.maven:maven-resolver-provider:3.9.6") // make API dependency for Paper Plugins compileOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18") compileOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18") diff --git a/paper-api/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java b/paper-api/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java deleted file mode 100644 index 2a169d2f6..000000000 --- a/paper-api/src/main/java/com/destroystokyo/paper/event/executor/MethodHandleEventExecutor.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.destroystokyo.paper.event.executor; - -import com.destroystokyo.paper.util.SneakyThrow; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; -import org.bukkit.event.Event; -import org.bukkit.event.EventException; -import org.bukkit.event.Listener; -import org.bukkit.plugin.EventExecutor; -import org.jetbrains.annotations.ApiStatus; -import org.jspecify.annotations.NullMarked; -import org.jspecify.annotations.Nullable; - -@ApiStatus.Internal -@NullMarked -public class MethodHandleEventExecutor implements EventExecutor { - - private final Class eventClass; - private final MethodHandle handle; - private final @Nullable Method method; - - public MethodHandleEventExecutor(final Class eventClass, final MethodHandle handle) { - this.eventClass = eventClass; - this.handle = handle; - this.method = null; - } - - public MethodHandleEventExecutor(final Class eventClass, final Method m) { - this.eventClass = eventClass; - try { - m.setAccessible(true); - this.handle = MethodHandles.lookup().unreflect(m); - } catch (final IllegalAccessException e) { - throw new AssertionError("Unable to set accessible", e); - } - this.method = m; - } - - @Override - public void execute(final Listener listener, final Event event) throws EventException { - if (!this.eventClass.isInstance(event)) return; - try { - this.handle.invoke(listener, event); - } catch (final Throwable t) { - SneakyThrow.sneaky(t); - } - } - - @Override - public String toString() { - return "MethodHandleEventExecutor['" + this.method + "']"; - } -} diff --git a/paper-api/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java b/paper-api/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java deleted file mode 100644 index e98962b6c..000000000 --- a/paper-api/src/main/java/com/destroystokyo/paper/event/executor/StaticMethodHandleEventExecutor.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.destroystokyo.paper.event.executor; - -import com.destroystokyo.paper.util.SneakyThrow; -import com.google.common.base.Preconditions; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import org.bukkit.event.Event; -import org.bukkit.event.EventException; -import org.bukkit.event.Listener; -import org.bukkit.plugin.EventExecutor; -import org.jetbrains.annotations.ApiStatus; -import org.jspecify.annotations.NullMarked; - -@ApiStatus.Internal -@NullMarked -public class StaticMethodHandleEventExecutor implements EventExecutor { - - private final Class eventClass; - private final MethodHandle handle; - private final Method method; - - public StaticMethodHandleEventExecutor(final Class eventClass, final Method m) { - Preconditions.checkArgument(Modifier.isStatic(m.getModifiers()), "Not a static method: %s", m); - Preconditions.checkArgument(eventClass != null, "eventClass is null"); - this.eventClass = eventClass; - try { - m.setAccessible(true); - this.handle = MethodHandles.lookup().unreflect(m); - } catch (final IllegalAccessException e) { - throw new AssertionError("Unable to set accessible", e); - } - this.method = m; - } - - @Override - public void execute(final Listener listener, final Event event) throws EventException { - if (!this.eventClass.isInstance(event)) return; - try { - this.handle.invoke(event); - } catch (final Throwable throwable) { - SneakyThrow.sneaky(throwable); - } - } - - @Override - public String toString() { - return "StaticMethodHandleEventExecutor['" + this.method + "']"; - } -} diff --git a/paper-api/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java b/paper-api/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java deleted file mode 100644 index abfcb6e83..000000000 --- a/paper-api/src/main/java/com/destroystokyo/paper/event/executor/asm/ASMEventExecutorGenerator.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.destroystokyo.paper.event.executor.asm; - -import java.lang.reflect.Method; -import java.util.concurrent.atomic.AtomicInteger; -import org.bukkit.plugin.EventExecutor; -import org.jetbrains.annotations.ApiStatus; -import org.jspecify.annotations.NullMarked; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Type; -import org.objectweb.asm.commons.GeneratorAdapter; - -import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; -import static org.objectweb.asm.Opcodes.INVOKESPECIAL; -import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; -import static org.objectweb.asm.Opcodes.V1_8; - -@ApiStatus.Internal -@NullMarked -public final class ASMEventExecutorGenerator { - - private static final String EXECUTE_DESCRIPTOR = "(Lorg/bukkit/event/Listener;Lorg/bukkit/event/Event;)V"; - - public static byte[] generateEventExecutor(final Method m, final String name) { - final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - writer.visit(V1_8, ACC_PUBLIC, name.replace('.', '/'), null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(EventExecutor.class)}); - // Generate constructor - GeneratorAdapter methodGenerator = new GeneratorAdapter(writer.visitMethod(ACC_PUBLIC, "", "()V", null, null), ACC_PUBLIC, "", "()V"); - methodGenerator.loadThis(); - methodGenerator.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V", false); // Invoke the super class (Object) constructor - methodGenerator.returnValue(); - methodGenerator.endMethod(); - // Generate the execute method - methodGenerator = new GeneratorAdapter(writer.visitMethod(ACC_PUBLIC, "execute", EXECUTE_DESCRIPTOR, null, null), ACC_PUBLIC, "execute", EXECUTE_DESCRIPTOR); - methodGenerator.loadArg(0); - methodGenerator.checkCast(Type.getType(m.getDeclaringClass())); - methodGenerator.loadArg(1); - methodGenerator.checkCast(Type.getType(m.getParameterTypes()[0])); - methodGenerator.visitMethodInsn(m.getDeclaringClass().isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, Type.getInternalName(m.getDeclaringClass()), m.getName(), Type.getMethodDescriptor(m), m.getDeclaringClass().isInterface()); - // The only purpose of this switch statement is to generate the correct pop instruction, should the event handler method return something other than void. - // Non-void event handlers will be unsupported in a future release. - switch (Type.getType(m.getReturnType()).getSize()) { - // case 0 is omitted because the only type that has size 0 is void - no pop instruction needed. - case 1 -> methodGenerator.pop(); // handles reference types and most primitives - case 2 -> methodGenerator.pop2(); // handles long and double - } - methodGenerator.returnValue(); - methodGenerator.endMethod(); - writer.visitEnd(); - return writer.toByteArray(); - } - - public static AtomicInteger NEXT_ID = new AtomicInteger(1); - - public static String generateName() { - final int id = NEXT_ID.getAndIncrement(); - return "com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor" + id; - } -} diff --git a/paper-api/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java b/paper-api/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java deleted file mode 100644 index 581561fbd..000000000 --- a/paper-api/src/main/java/com/destroystokyo/paper/event/executor/asm/ClassDefiner.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.destroystokyo.paper.event.executor.asm; - -import org.jetbrains.annotations.ApiStatus; -import org.jspecify.annotations.NullMarked; - -@ApiStatus.Internal -@NullMarked -public interface ClassDefiner { - - /** - * Returns if the defined classes can bypass access checks - * - * @return if classes bypass access checks - */ - default boolean isBypassAccessChecks() { - return false; - } - - /** - * Define a class - * - * @param parentLoader the parent classloader - * @param name the name of the class - * @param data the class data to load - * @return the defined class - * @throws ClassFormatError if the class data is invalid - * @throws NullPointerException if any of the arguments are null - */ - Class defineClass(ClassLoader parentLoader, String name, byte[] data); - - static ClassDefiner getInstance() { - return SafeClassDefiner.INSTANCE; - } - -} diff --git a/paper-api/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java b/paper-api/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java deleted file mode 100644 index 48bcc7229..000000000 --- a/paper-api/src/main/java/com/destroystokyo/paper/event/executor/asm/SafeClassDefiner.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.destroystokyo.paper.event.executor.asm; - -import com.google.common.base.Preconditions; -import com.google.common.collect.MapMaker; -import java.util.concurrent.ConcurrentMap; -import org.jetbrains.annotations.ApiStatus; -import org.jspecify.annotations.NullMarked; - -@ApiStatus.Internal -@NullMarked -public class SafeClassDefiner implements ClassDefiner { - - /* default */ static final SafeClassDefiner INSTANCE = new SafeClassDefiner(); - - private SafeClassDefiner() { - } - - private final ConcurrentMap loaders = new MapMaker().weakKeys().makeMap(); - - @Override - public Class defineClass(final ClassLoader parentLoader, final String name, final byte[] data) { - final GeneratedClassLoader loader = this.loaders.computeIfAbsent(parentLoader, GeneratedClassLoader::new); - synchronized (loader.getClassLoadingLock(name)) { - Preconditions.checkState(!loader.hasClass(name), "%s already defined", name); - final Class c = loader.define(name, data); - assert c.getName().equals(name); - return c; - } - } - - private static class GeneratedClassLoader extends ClassLoader { - - static { - ClassLoader.registerAsParallelCapable(); - } - - protected GeneratedClassLoader(final ClassLoader parent) { - super(parent); - } - - private Class define(final String name, final byte[] data) { - synchronized (this.getClassLoadingLock(name)) { - assert !this.hasClass(name); - final Class c = this.defineClass(name, data, 0, data.length); - this.resolveClass(c); - return c; - } - } - - @Override - public Object getClassLoadingLock(final String name) { - return super.getClassLoadingLock(name); - } - - public boolean hasClass(final String name) { - synchronized (this.getClassLoadingLock(name)) { - try { - Class.forName(name); - return true; - } catch (final ClassNotFoundException e) { - return false; - } - } - } - } -} diff --git a/paper-api/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java b/paper-api/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java index acff2ff57..f87e304f8 100644 --- a/paper-api/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java +++ b/paper-api/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java @@ -304,8 +304,7 @@ public class PaperServerListPingEvent extends ServerListPingEvent implements Can return new PlayerIterator(); } - @NotNull - protected Object[] getOnlinePlayers() { + protected @NotNull Object @NotNull [] getOnlinePlayers() { return Bukkit.getOnlinePlayers().toArray(); } diff --git a/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java new file mode 100644 index 000000000..511da534a --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java @@ -0,0 +1,39 @@ +package io.papermc.paper; + +import net.kyori.adventure.util.Services; +import org.bukkit.damage.DamageEffect; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Static bridge to the server internals. + *

+ * Any and all methods in here are *not* to be called by plugin developers, may change at any time and may generally + * cause issues when called under unexpected circumstances. + */ +@ApiStatus.Internal +@NullMarked +public interface InternalAPIBridge { + + /** + * Yields the instance of this API bridge by lazily requesting it from the java service loader API. + * + * @return the instance. + */ + static InternalAPIBridge get() { + class Holder { + public static final InternalAPIBridge INSTANCE = Services.service(InternalAPIBridge.class).orElseThrow(); + } + + return Holder.INSTANCE; + } + + /** + * Creates a damage effect instance for the passed key. + * + * @param key the string key. + * @return the damage effect. + */ + DamageEffect getDamageEffect(String key); +} + diff --git a/paper-api/src/main/java/io/papermc/paper/datapack/Datapack.java b/paper-api/src/main/java/io/papermc/paper/datapack/Datapack.java index 95039ec90..fff0cdb98 100644 --- a/paper-api/src/main/java/io/papermc/paper/datapack/Datapack.java +++ b/paper-api/src/main/java/io/papermc/paper/datapack/Datapack.java @@ -1,10 +1,8 @@ package io.papermc.paper.datapack; -import java.util.Set; import net.kyori.adventure.text.Component; -import org.bukkit.FeatureFlag; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.Unmodifiable; import org.jspecify.annotations.NullMarked; /** @@ -12,50 +10,8 @@ import org.jspecify.annotations.NullMarked; * won't be updated as datapacks are updated. */ @NullMarked -public interface Datapack { - - /** - * Gets the name/id of this datapack. - * - * @return the name of the pack - */ - @Contract(pure = true) - String getName(); - - /** - * Gets the title component of this datapack. - * - * @return the title - */ - Component getTitle(); - - /** - * Gets the description component of this datapack. - * - * @return the description - */ - Component getDescription(); - - /** - * Gets if this datapack is required to be enabled. - * - * @return true if the pack is required - */ - boolean isRequired(); - - /** - * Gets the compatibility status of this pack. - * - * @return the compatibility of the pack - */ - Compatibility getCompatibility(); - - /** - * Gets the set of required features for this datapack. - * - * @return the set of required features - */ - @Unmodifiable Set getRequiredFeatures(); +@ApiStatus.NonExtendable +public interface Datapack extends DiscoveredDatapack { /** * Gets the enabled state of this pack. @@ -74,13 +30,6 @@ public interface Datapack { */ void setEnabled(boolean enabled); - /** - * Gets the source for this datapack. - * - * @return the pack source - */ - DatapackSource getSource(); - /** * Computes the component vanilla Minecraft uses * to display this datapack. Includes the {@link #getSource()}, @@ -96,4 +45,11 @@ public interface Datapack { TOO_NEW, COMPATIBLE, } + + /** + * Position of the pack in the load order. + */ + enum Position { + TOP, BOTTOM + } } diff --git a/paper-api/src/main/java/io/papermc/paper/datapack/DatapackRegistrar.java b/paper-api/src/main/java/io/papermc/paper/datapack/DatapackRegistrar.java new file mode 100644 index 000000000..b009df66d --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/datapack/DatapackRegistrar.java @@ -0,0 +1,202 @@ +package io.papermc.paper.datapack; + +import io.papermc.paper.plugin.configuration.PluginMeta; +import io.papermc.paper.plugin.lifecycle.event.registrar.Registrar; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.Map; +import java.util.function.Consumer; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * The registrar for datapacks. The event for this registrar + * is called anytime the game tries to discover datapacks at any of the + * configured locations. This means that if a datapack should stay available to the server, + * it must always be discovered whenever this event fires. + *

An example of a plugin loading a datapack from within it's own jar is below

+ *
{@code
+ * public class YourPluginBootstrap implements PluginBootstrap {
+ *     @Override
+ *     public void bootstrap(BoostrapContext context) {
+ *         final LifecycleEventManager manager = context.getLifecycleManager();
+ *         manager.registerEventHandler(LifecycleEvents.DATAPACK_DISCOVERY, event -> {
+ *             DatapackRegistrar registrar = event.registrar();
+ *             try {
+ *                 final URI uri = Objects.requireNonNull(
+ *                     YourPluginBootstrap.class.getResource("/pack")
+ *                 ).toURI();
+ *                 registrar.discoverPack(uri, "packId");
+ *             } catch (final URISyntaxException | IOException e) {
+ *                 throw new RuntimeException(e);
+ *             }
+ *         });
+ *     }
+ * }
+ * }
+ * @see io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents#DATAPACK_DISCOVERY + */ +@ApiStatus.NonExtendable +@ApiStatus.Experimental +@NullMarked +public interface DatapackRegistrar extends Registrar { + + /** + * Checks if a datapack with the specified name has been discovered. + * + * @param name the name of the pack + * @return true if the pack has been discovered + * @see Datapack#getName() + */ + @Contract(pure = true) + boolean hasPackDiscovered(String name); + + /** + * Gets a discovered datapack by its name. + * + * @param name the name of the pack + * @return the datapack + * @throws java.util.NoSuchElementException if the pack is not discovered + * @see Datapack#getName() + */ + @Contract(pure = true) + DiscoveredDatapack getDiscoveredPack(String name); + + /** + * Removes a discovered datapack by its name. + * + * @param name the name of the pack + * @return true if the pack was removed + * @see Datapack#getName() + */ + @Contract(mutates = "this") + boolean removeDiscoveredPack(String name); + + /** + * Gets all discovered datapacks. + * + * @return an unmodifiable map of discovered packs + */ + @Contract(pure = true) + @Unmodifiable Map getDiscoveredPacks(); + + /** + * Discovers a datapack at the specified {@link URI} with the id. + *

Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.

+ * + * @param uri the location of the pack + * @param id a unique id (will be combined with plugin for the datapacks name) + * @return the discovered datapack (or null if it failed) + * @throws IOException if any IO error occurs + */ + default @Nullable DiscoveredDatapack discoverPack(final URI uri, final String id) throws IOException { + return this.discoverPack(uri, id, c -> {}); + } + + /** + * Discovers a datapack at the specified {@link URI} with the id. + *

Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.

+ * + * @param uri the location of the pack + * @param id a unique id (will be combined with plugin for the datapacks name) + * @param configurer a configurer for extra options + * @return the discovered datapack (or null if it failed) + * @throws IOException if any IO error occurs + */ + @Nullable DiscoveredDatapack discoverPack(URI uri, String id, Consumer configurer) throws IOException; + + /** + * Discovers a datapack at the specified {@link Path} with the id. + *

Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.

+ * + * @param path the location of the pack + * @param id a unique id (will be combined with plugin for the datapacks name) + * @return the discovered datapack (or null if it failed) + * @throws IOException if any IO error occurs + */ + default @Nullable DiscoveredDatapack discoverPack(final Path path, final String id) throws IOException { + return this.discoverPack(path, id, c -> {}); + } + + /** + * Discovers a datapack at the specified {@link Path} with the id. + *

Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.

+ * + * @param path the location of the pack + * @param id a unique id (will be combined with plugin for the datapacks name) + * @param configurer a configurer for extra options + * @return the discovered datapack (or null if it failed) + * @throws IOException if any IO error occurs + */ + @Nullable DiscoveredDatapack discoverPack(Path path, String id, Consumer configurer) throws IOException; + + /** + * Discovers a datapack at the specified {@link URI} with the id. + *

Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.

+ * + * @param pluginMeta the plugin which will be the "owner" of this datapack + * @param uri the location of the pack + * @param id a unique id (will be combined with plugin for the datapacks name) + * @param configurer a configurer for extra options + * @return the discovered datapack (or null if it failed) + * @throws IOException if any IO error occurs + */ + @Nullable DiscoveredDatapack discoverPack(PluginMeta pluginMeta, URI uri, String id, Consumer configurer) throws IOException; + + /** + * Discovers a datapack at the specified {@link Path} with the id. + *

Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.

+ * + * @param pluginMeta the plugin which will be the "owner" of this datapack + * @param path the location of the pack + * @param id a unique id (will be combined with plugin for the datapacks name) + * @param configurer a configurer for extra options + * @return the discovered datapack (or null if it failed) + * @throws IOException if any IO error occurs + */ + @Nullable DiscoveredDatapack discoverPack(PluginMeta pluginMeta, Path path, String id, Consumer configurer) throws IOException; + + /** + * Configures additional, optional, details about a datapack. + */ + @ApiStatus.NonExtendable + @ApiStatus.Experimental + interface Configurer { + + /** + * Changes the title of the datapack from the default which + * is just the "id" in the {@code registerPack} methods. + * + * @param title the new title + * @return the configurer for chaining + */ + @Contract(value = "_ -> this", mutates = "this") + Configurer title(Component title); + + /** + * Sets whether this pack is going to be automatically enabled on server starts even if previously disabled. + * Defaults to false. + * + * @param autoEnableOnServerStart true to ensure the pack is enabled on server starts. + * @return the configurer for chaining + */ + @Contract(value = "_ -> this", mutates = "this") + Configurer autoEnableOnServerStart(boolean autoEnableOnServerStart); + + /** + * Configures the position in the + * load order of this datapack. + * + * @param fixed won't move around in the load order as packs are added/removed + * @param position try to insert at the top of the order or bottom + * @return the configurer for chaining + */ + @Contract(value = "_, _ -> this", mutates = "this") + Configurer position(boolean fixed, Datapack.Position position); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/datapack/DatapackSource.java b/paper-api/src/main/java/io/papermc/paper/datapack/DatapackSource.java index b9b81cd97..1739d598f 100644 --- a/paper-api/src/main/java/io/papermc/paper/datapack/DatapackSource.java +++ b/paper-api/src/main/java/io/papermc/paper/datapack/DatapackSource.java @@ -13,6 +13,7 @@ public sealed interface DatapackSource permits DatapackSourceImpl { DatapackSource FEATURE = create("feature"); DatapackSource WORLD = create("world"); DatapackSource SERVER = create("server"); + DatapackSource PLUGIN = create("plugin"); private static DatapackSource create(final String name) { return new DatapackSourceImpl(name); diff --git a/paper-api/src/main/java/io/papermc/paper/datapack/DiscoveredDatapack.java b/paper-api/src/main/java/io/papermc/paper/datapack/DiscoveredDatapack.java new file mode 100644 index 000000000..984507ba0 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/datapack/DiscoveredDatapack.java @@ -0,0 +1,74 @@ +package io.papermc.paper.datapack; + +import java.util.Set; +import net.kyori.adventure.text.Component; +import org.bukkit.FeatureFlag; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NullMarked; + +/** + * Represents a snapshot of a datapack that the server has found by + * searching available sources. It may or may not be enabled and isn't + * guaranteed to be available. This object won't be + * updated as datapacks are updated. + * @see DatapackRegistrar + */ +@NullMarked +@ApiStatus.NonExtendable +public interface DiscoveredDatapack { + + /** + * Gets the name/id of this datapack. + * + * @return the name of the pack + */ + @Contract(pure = true) + String getName(); + + /** + * Gets the title component of this datapack. + * + * @return the title + */ + Component getTitle(); + + /** + * Gets the description component of this datapack. + * + * @return the description + */ + Component getDescription(); + + /** + * Gets if this datapack is required. + *

+ * A "required" datapack will always be enabled on server startup, even if previously disabled. + * + * @return true if the pack is required + */ + boolean isRequired(); + + /** + * Gets the compatibility status of this pack. + * + * @return the compatibility of the pack + */ + Datapack.Compatibility getCompatibility(); + + /** + * Gets the set of required features for this datapack. + * + * @return the set of required features + */ + @Unmodifiable + Set getRequiredFeatures(); + + /** + * Gets the source for this datapack. + * + * @return the pack source + */ + DatapackSource getSource(); +} diff --git a/paper-api/src/main/java/io/papermc/paper/entity/EntitySerializationFlag.java b/paper-api/src/main/java/io/papermc/paper/entity/EntitySerializationFlag.java new file mode 100644 index 000000000..4a76c3491 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/entity/EntitySerializationFlag.java @@ -0,0 +1,38 @@ +package io.papermc.paper.entity; + +import org.bukkit.UnsafeValues; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +/** + * Represents flags for entity serialization. + * + * @see UnsafeValues#serializeEntity(Entity, EntitySerializationFlag... serializationFlags) + * @since 1.21.4 + */ +public enum EntitySerializationFlag { + + /** + * Serialize entities that wouldn't be serialized normally + * (e.g. dead, despawned, non-persistent, etc.). + * + * @see Entity#isValid() + * @see Entity#isPersistent() + */ + FORCE, + /** + * Serialize misc non-saveable entities like lighting bolts, fishing bobbers, etc. + *
Note: players require a separate flag: {@link #PLAYER}. + */ + MISC, + /** + * Include passengers in the serialized data. + */ + PASSENGERS, + /** + * Allow serializing {@link Player}s. + *

Note: deserializing player data will always fail. + */ + PLAYER + +} diff --git a/paper-api/src/main/java/io/papermc/paper/event/executor/EventExecutorFactory.java b/paper-api/src/main/java/io/papermc/paper/event/executor/EventExecutorFactory.java new file mode 100644 index 000000000..2e3e21c3e --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/event/executor/EventExecutorFactory.java @@ -0,0 +1,74 @@ +package io.papermc.paper.event.executor; + +import org.bukkit.event.Event; +import org.bukkit.event.Listener; +import org.bukkit.plugin.EventExecutor; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import java.io.IOException; +import java.io.InputStream; +import java.lang.constant.ConstantDescs; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.Objects; + +@ApiStatus.Internal +@NullMarked +public final class EventExecutorFactory { + private static final byte[] TEMPLATE_CLASS_BYTES; + + static { + try (final InputStream is = EventExecutorFactory.class.getResourceAsStream("MethodHandleEventExecutorTemplate.class")) { + TEMPLATE_CLASS_BYTES = Objects.requireNonNull(is, "template class is missing").readAllBytes(); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + private EventExecutorFactory() { + + } + + /** + * {@return an {@link EventExecutor} implemented by a hidden class calling a method handle} + * + * @param method the method to be invoked by the created event executor + * @param eventClass the class of the event to handle + */ + public static EventExecutor create(final Method method, final Class eventClass) { + final List classData = List.of(method, eventClass); + try { + final MethodHandles.Lookup newClass = MethodHandles.lookup().defineHiddenClassWithClassData(TEMPLATE_CLASS_BYTES, classData, true); + return newClass.lookupClass().asSubclass(EventExecutor.class).getDeclaredConstructor().newInstance(); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } + + record ClassData(Method method, MethodHandle methodHandle, Class eventClass) { + + } + + /** + * Extracts the class data and creates an adjusted MethodHandle directly usable by the lookup class. + * The logic is kept here to minimize memory usage per created class. + */ + static ClassData classData(final MethodHandles.Lookup lookup) { + try { + final Method method = MethodHandles.classDataAt(lookup, ConstantDescs.DEFAULT_NAME, Method.class, 0); + MethodHandle mh = lookup.unreflect(method); + if (Modifier.isStatic(method.getModifiers())) { + mh = MethodHandles.dropArguments(mh, 0, Listener.class); + } + mh = mh.asType(MethodType.methodType(void.class, Listener.class, Event.class)); + final Class eventClass = MethodHandles.classDataAt(lookup, ConstantDescs.DEFAULT_NAME, Class.class, 1); + return new ClassData(method, mh, eventClass.asSubclass(Event.class)); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/event/executor/MethodHandleEventExecutorTemplate.java b/paper-api/src/main/java/io/papermc/paper/event/executor/MethodHandleEventExecutorTemplate.java new file mode 100644 index 000000000..b28fcfed9 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/event/executor/MethodHandleEventExecutorTemplate.java @@ -0,0 +1,56 @@ +package io.papermc.paper.event.executor; + +import com.destroystokyo.paper.util.SneakyThrow; +import org.bukkit.event.Event; +import org.bukkit.event.EventException; +import org.bukkit.event.Listener; +import org.bukkit.plugin.EventExecutor; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; + +/** + * This class is designed to be used as hidden class template. + * Initializing the class directly will fail due to missing {@code classData}. + * Instead, {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClassWithClassData(byte[], Object, boolean, MethodHandles.Lookup.ClassOption...)} + * must be used, with the {@code classData} object being a list consisting of two elements: + *

    + *
  1. A {@link Method} representing the event handler method
  2. + *
  3. A {@link Class} representing the event type
  4. + *
+ * The method must take {@link Event} or a subtype of it as its single parameter. + * If the method is non-static, it also needs to reside in a class implementing {@link Listener}. + */ +@SuppressWarnings("unused") +@ApiStatus.Internal +@NullMarked +class MethodHandleEventExecutorTemplate implements EventExecutor { + private static final Method METHOD; + private static final MethodHandle HANDLE; + private static final Class EVENT_CLASS; + + static { + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + final EventExecutorFactory.ClassData classData = EventExecutorFactory.classData(lookup); + METHOD = classData.method(); + HANDLE = classData.methodHandle(); + EVENT_CLASS = classData.eventClass(); + } + + @Override + public void execute(final Listener listener, final Event event) throws EventException { + if (!EVENT_CLASS.isInstance(event)) return; + try { + HANDLE.invokeExact(listener, event); + } catch (Throwable t) { + SneakyThrow.sneaky(t); + } + } + + @Override + public String toString() { + return "MethodHandleEventExecutorTemplate['" + METHOD + "']"; + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java b/paper-api/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java index b65c5fd54..8ff52773b 100644 --- a/paper-api/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java +++ b/paper-api/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java @@ -1,10 +1,12 @@ package io.papermc.paper.plugin.lifecycle.event.types; import io.papermc.paper.command.brigadier.Commands; +import io.papermc.paper.datapack.DatapackRegistrar; import io.papermc.paper.plugin.bootstrap.BootstrapContext; import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent; import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager; import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner; +import io.papermc.paper.plugin.lifecycle.event.registrar.RegistrarEvent; import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.ApiStatus; @@ -32,6 +34,14 @@ public final class LifecycleEvents { */ public static final TagEventTypeProvider TAGS = LifecycleEventTypeProvider.provider().tagProvider(); + + /** + * This event is for informing the server about any available datapacks from other sources such as inside a plugin's jar. You + * can register a handler for this event only in {@link io.papermc.paper.plugin.bootstrap.PluginBootstrap#bootstrap(BootstrapContext)}. + * @see DatapackRegistrar an example of a datapack being discovered + */ + public static final LifecycleEventType.Prioritizable> DATAPACK_DISCOVERY = bootstrapPrioritized("datapack_discovery"); + // @ApiStatus.Internal static LifecycleEventType.Monitorable plugin(final String name) { diff --git a/paper-api/src/main/java/io/papermc/paper/registry/RegistryBuilderFactory.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryBuilderFactory.java new file mode 100644 index 000000000..ce2447d3d --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/registry/RegistryBuilderFactory.java @@ -0,0 +1,39 @@ +package io.papermc.paper.registry; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * A factory to create a {@link RegistryBuilder} for a given {@link TypedKey}. For + * each instance of this class, once either {@link #empty()} or {@link #copyFrom(TypedKey)} + * is called once, any future calls to either method will throw an {@link IllegalStateException}. + * + * @param The type of the registry + * @param The type of the registry builder + */ +@NullMarked +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface RegistryBuilderFactory> { + + /** + * Creates a new empty {@link RegistryBuilder}. + * + * @return A new empty {@link RegistryBuilder} + * @throws IllegalStateException if this method or {@link #copyFrom(TypedKey)}) has already been called once + */ + @Contract("-> new") + B empty(); + + /** + * Creates a new {@link RegistryBuilder} with the same properties as the given {@link TypedKey}. + * + * @param key The key to copy properties from + * @return A new {@link RegistryBuilder} with the same properties as the given key + * @throws IllegalStateException if this method or {@link #empty()} has already been called once + * @throws IllegalArgumentException if key doesn't exist + */ + @Contract("_ -> new") + B copyFrom(TypedKey key); +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java index e5319bdb9..ea795de95 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java @@ -1,6 +1,8 @@ package io.papermc.paper.registry; import io.papermc.paper.datacomponent.DataComponentType; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; import net.kyori.adventure.key.Keyed; import org.bukkit.Art; import org.bukkit.Fluid; @@ -200,4 +202,26 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { RegistryKey PARTICLE_TYPE = create("particle_type"); RegistryKey POTION = create("potion"); RegistryKey> MEMORY_MODULE_TYPE = create("memory_module_type"); + + /** + * Constructs a new {@link TypedKey} for this registry given the typed key's key. + * + * @param key the key of the typed key. + * @return the constructed typed key. + */ + @ApiStatus.Experimental + default TypedKey typedKey(final Key key) { + return TypedKey.create(this, key); + } + + /** + * Constructs a new {@link TypedKey} for this registry given the typed key's key. + * + * @param key the string representation of the key that will be passed to {@link Key#key(String)}. + * @return the constructed typed key. + */ + @ApiStatus.Experimental + default TypedKey typedKey(final @KeyPattern String key) { + return TypedKey.create(this, key); + } } diff --git a/paper-api/src/main/java/io/papermc/paper/registry/TypedKey.java b/paper-api/src/main/java/io/papermc/paper/registry/TypedKey.java index 81bee5224..c8f363a24 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/TypedKey.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/TypedKey.java @@ -1,6 +1,7 @@ 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; @@ -42,4 +43,18 @@ public sealed interface TypedKey extends Key permits TypedKeyImpl { static TypedKey create(final RegistryKey registryKey, final Key key) { return new TypedKeyImpl<>(key, registryKey); } + + /** + * Create a typed key from a string and a registry key. + * + * @param registryKey the registry this key is for + * @param key the string version of a {@link Key} that will be passed to {@link Key#key(String)} for parsing. + * @param value type + * @return a new key for the value key and registry key + * @see Key#key(String) + */ + @ApiStatus.Experimental + static TypedKey create(final RegistryKey registryKey, final @KeyPattern String key) { + return create(registryKey, Key.key(key)); + } } diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/BannerPatternRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/BannerPatternRegistryEntry.java new file mode 100644 index 000000000..8ca672a5b --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/BannerPatternRegistryEntry.java @@ -0,0 +1,65 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.registry.RegistryBuilder; +import net.kyori.adventure.key.Key; +import org.bukkit.block.banner.PatternType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; + +/** + * A data-centric version-specific registry entry for the {@link PatternType} type. + */ +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface BannerPatternRegistryEntry { + + /** + * Provides the asset id of the pattern type, which is the location of the sprite to use. + * + * @return the asset id. + */ + Key assetId(); + + /** + * Provides the translation key for displaying the pattern inside the banner's tooltip. + * + * @return the translation key. + */ + String translationKey(); + + /** + * A mutable builder for the {@link BannerPatternRegistryEntry} plugins may change in applicable registry events. + *

+ * The following values are required for each builder: + *

    + *
  • {@link #assetId(Key)}
  • + *
  • {@link #translationKey(String)}
  • + *
+ */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends BannerPatternRegistryEntry, RegistryBuilder { + + /** + * Sets the asset id of the pattern type, which is the location of the sprite to use. + * + * @param assetId the asset id. + * @return this builder instance. + * @see BannerPatternRegistryEntry#assetId() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder assetId(Key assetId); + + /** + * Sets the translation key for displaying the pattern inside the banner's tooltip. + * + * @param translationKey the translation key. + * @return this builder instance. + * @see BannerPatternRegistryEntry#translationKey() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder translationKey(String translationKey); + + } + +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/DamageTypeRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/DamageTypeRegistryEntry.java new file mode 100644 index 000000000..fd2d7fffa --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/DamageTypeRegistryEntry.java @@ -0,0 +1,120 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.registry.RegistryBuilder; +import org.bukkit.damage.DamageEffect; +import org.bukkit.damage.DamageScaling; +import org.bukkit.damage.DamageType; +import org.bukkit.damage.DeathMessageType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; + +/** + * A data-centric version-specific registry entry for the {@link DamageType} type. + */ +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface DamageTypeRegistryEntry { + + /** + * Provides part of the death message translation key. (death.attack.<message_id>) + *

+ * Note The translation key is only used if + * {@link #deathMessageType()} is {@link DeathMessageType#DEFAULT} + * + * @return part of the translation key + */ + String messageId(); + + /** + * Provides the amount of hunger exhaustion caused by this damage type. + * + * @return the exhaustion + */ + float exhaustion(); + + /** + * Provides the {@link DamageScaling} for this damage type. + * + * @return the damage scaling + */ + DamageScaling damageScaling(); + + /** + * Provides the {@link DamageEffect} for this damage type. + * + * @return the damage effect + */ + DamageEffect damageEffect(); + + /** + * Provides the {@link DeathMessageType} for this damage type. + * + * @return the death message type + */ + DeathMessageType deathMessageType(); + + /** + * A mutable builder for the {@link DamageTypeRegistryEntry} plugins may change in applicable registry events. + *

+ * The following values are required for each builder: + *

    + *
  • {@link #messageId(String)}
  • + *
  • {@link #exhaustion(float)}
  • + *
  • {@link #damageScaling(DamageScaling)}
  • + *
+ */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends DamageTypeRegistryEntry, RegistryBuilder { + + /** + * Sets part of the death message translation key. + * + * @return this builder instance. + * @see DamageTypeRegistryEntry#messageId() + * @see DamageType#getTranslationKey() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder messageId(String messageId); + + /** + * Sets the amount of hunger exhaustion caused by this damage type. + * + * @return this builder instance. + * @see DamageTypeRegistryEntry#exhaustion() + * @see DamageType#getExhaustion() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder exhaustion(float exhaustion); + + /** + * Sets the {@link DamageScaling} for this damage type. + * + * @return this builder instance. + * @see DamageTypeRegistryEntry#damageScaling() + * @see DamageType#getDamageScaling() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder damageScaling(DamageScaling scaling); + + /** + * Sets the {@link DamageEffect} for this damage type. + * + * @return this builder instance. + * @see DamageTypeRegistryEntry#damageEffect() + * @see DamageType#getDamageEffect() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder damageEffect(DamageEffect effect); + + /** + * Sets the {@link DeathMessageType} for this damage type. + * + * @return this builder instance. + * @see DamageTypeRegistryEntry#deathMessageType() + * @see DamageType#getDeathMessageType() + */ + @Contract(value = "_ -> this", mutates = "this") + Builder deathMessageType(DeathMessageType deathMessageType); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/InlinedRegistryBuilderProvider.java b/paper-api/src/main/java/io/papermc/paper/registry/data/InlinedRegistryBuilderProvider.java new file mode 100644 index 000000000..554bda034 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/InlinedRegistryBuilderProvider.java @@ -0,0 +1,22 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.registry.RegistryBuilderFactory; +import java.util.Optional; +import java.util.ServiceLoader; +import java.util.function.Consumer; +import org.bukkit.Art; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +@ApiStatus.NonExtendable +public interface InlinedRegistryBuilderProvider { + + static InlinedRegistryBuilderProvider instance() { + class Holder { + static final Optional INSTANCE = ServiceLoader.load(InlinedRegistryBuilderProvider.class).findFirst(); + } + return Holder.INSTANCE.orElseThrow(); + } + + Art createPaintingVariant(Consumer> value); +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java b/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java index 40deffbd0..d15581579 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java @@ -1,11 +1,15 @@ package io.papermc.paper.registry.event; import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.BannerPatternRegistryEntry; +import io.papermc.paper.registry.data.DamageTypeRegistryEntry; import io.papermc.paper.registry.data.EnchantmentRegistryEntry; import io.papermc.paper.registry.data.GameEventRegistryEntry; import io.papermc.paper.registry.data.PaintingVariantRegistryEntry; import org.bukkit.Art; import org.bukkit.GameEvent; +import org.bukkit.block.banner.PatternType; +import org.bukkit.damage.DamageType; import org.bukkit.enchantments.Enchantment; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; @@ -23,6 +27,8 @@ public final class RegistryEvents { public static final RegistryEventProvider GAME_EVENT = create(RegistryKey.GAME_EVENT); public static final RegistryEventProvider ENCHANTMENT = create(RegistryKey.ENCHANTMENT); public static final RegistryEventProvider PAINTING_VARIANT = create(RegistryKey.PAINTING_VARIANT); + public static final RegistryEventProvider BANNER_PATTERN = create(RegistryKey.BANNER_PATTERN); + public static final RegistryEventProvider DAMAGE_TYPE = create(RegistryKey.DAMAGE_TYPE); private RegistryEvents() { } diff --git a/paper-api/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java b/paper-api/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java index 744f455b1..fed7ad660 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java @@ -1,6 +1,7 @@ package io.papermc.paper.registry.event; import io.papermc.paper.registry.RegistryBuilder; +import io.papermc.paper.registry.RegistryBuilderFactory; import io.papermc.paper.registry.TypedKey; import java.util.function.Consumer; import org.jetbrains.annotations.ApiStatus; @@ -24,5 +25,18 @@ public interface WritableRegistry> { * @param key the entry's key (must be unique from others) * @param value a consumer for the entry's builder */ - void register(TypedKey key, Consumer value); + default void register(final TypedKey key, final Consumer value) { + this.registerWith(key, factory -> value.accept(factory.empty())); + } + + /** + * Register a new value with the specified key. This will + * fire a {@link RegistryEntryAddEvent} for the new entry. The + * {@link RegistryBuilderFactory} lets you pre-fill a builder with + * an already-existing entry's properties. + * + * @param key the entry's key (must be unique from others) + * @param value a consumer of a builder factory + */ + void registerWith(TypedKey key, Consumer> value); } diff --git a/paper-api/src/main/java/org/bukkit/Art.java b/paper-api/src/main/java/org/bukkit/Art.java index 5d3fb60a2..c06204489 100644 --- a/paper-api/src/main/java/org/bukkit/Art.java +++ b/paper-api/src/main/java/org/bukkit/Art.java @@ -2,9 +2,14 @@ package org.bukkit; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; +import io.papermc.paper.registry.RegistryBuilderFactory; +import io.papermc.paper.registry.data.InlinedRegistryBuilderProvider; +import io.papermc.paper.registry.data.PaintingVariantRegistryEntry; import java.util.Locale; +import java.util.function.Consumer; import org.bukkit.packs.DataPack; import org.bukkit.util.OldEnum; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -18,6 +23,17 @@ import org.jetbrains.annotations.Nullable; */ public interface Art extends OldEnum, Keyed { + /** + * Create an inlined painting variant. + * + * @param value a consumer for the builder factory + * @return the created painting variant + */ + @ApiStatus.Experimental + static @NotNull Art create(final @NotNull Consumer> value) { + return InlinedRegistryBuilderProvider.instance().createPaintingVariant(value); + } + Art KEBAB = getArt("kebab"); Art AZTEC = getArt("aztec"); Art ALBAN = getArt("alban"); diff --git a/paper-api/src/main/java/org/bukkit/Bukkit.java b/paper-api/src/main/java/org/bukkit/Bukkit.java index 8ab94f818..9eecd3a68 100644 --- a/paper-api/src/main/java/org/bukkit/Bukkit.java +++ b/paper-api/src/main/java/org/bukkit/Bukkit.java @@ -1184,7 +1184,7 @@ public final class Bukkit { * @return the {@link Recipe} resulting from the given crafting matrix. */ @Nullable - public static Recipe getCraftingRecipe(@NotNull ItemStack[] craftingMatrix, @NotNull World world) { + public static Recipe getCraftingRecipe(@NotNull ItemStack @NotNull [] craftingMatrix, @NotNull World world) { return server.getCraftingRecipe(craftingMatrix, world); } @@ -1213,7 +1213,7 @@ public final class Bukkit { * @return resulting {@link ItemCraftResult} containing the resulting item, matrix and any overflow items. */ @NotNull - public static ItemCraftResult craftItemResult(@NotNull ItemStack[] craftingMatrix, @NotNull World world, @NotNull Player player) { + public static ItemCraftResult craftItemResult(@NotNull ItemStack @NotNull [] craftingMatrix, @NotNull World world, @NotNull Player player) { return server.craftItemResult(craftingMatrix, world, player); } @@ -1235,7 +1235,7 @@ public final class Bukkit { * @return resulting {@link ItemCraftResult} containing the resulting item, matrix and any overflow items. */ @NotNull - public static ItemCraftResult craftItemResult(@NotNull ItemStack[] craftingMatrix, @NotNull World world) { + public static ItemCraftResult craftItemResult(@NotNull ItemStack @NotNull [] craftingMatrix, @NotNull World world) { return server.craftItemResult(craftingMatrix, world); } @@ -1266,7 +1266,7 @@ public final class Bukkit { * an ItemStack of {@link Material#AIR} is returned. */ @NotNull - public static ItemStack craftItem(@NotNull ItemStack[] craftingMatrix, @NotNull World world, @NotNull Player player) { + public static ItemStack craftItem(@NotNull ItemStack @NotNull [] craftingMatrix, @NotNull World world, @NotNull Player player) { return server.craftItem(craftingMatrix, world, player); } @@ -1289,7 +1289,7 @@ public final class Bukkit { * an ItemStack of {@link Material#AIR} is returned. */ @NotNull - public static ItemStack craftItem(@NotNull ItemStack[] craftingMatrix, @NotNull World world) { + public static ItemStack craftItem(@NotNull ItemStack @NotNull [] craftingMatrix, @NotNull World world) { return server.craftItem(craftingMatrix, world); } @@ -1756,8 +1756,7 @@ public final class Bukkit { * * @return an array containing all previous players */ - @NotNull - public static OfflinePlayer[] getOfflinePlayers() { + public static @NotNull OfflinePlayer @NotNull [] getOfflinePlayers() { return server.getOfflinePlayers(); } @@ -2404,8 +2403,7 @@ public final class Bukkit { * Gets the current server TPS * @return current server TPS (1m, 5m, 15m in Paper-Server) */ - @NotNull - public static double[] getTPS() { + public static double @NotNull [] getTPS() { return server.getTPS(); } @@ -2414,8 +2412,7 @@ public final class Bukkit { * * @return A sample of the servers last tick times (in nanos) */ - @NotNull - public static long[] getTickTimes() { + public static long @NotNull [] getTickTimes() { return server.getTickTimes(); } diff --git a/paper-api/src/main/java/org/bukkit/Chunk.java b/paper-api/src/main/java/org/bukkit/Chunk.java index bc8b5bc17..d43427734 100644 --- a/paper-api/src/main/java/org/bukkit/Chunk.java +++ b/paper-api/src/main/java/org/bukkit/Chunk.java @@ -133,8 +133,7 @@ public interface Chunk extends PersistentDataHolder { * * @return The entities. */ - @NotNull - Entity[] getEntities(); + @NotNull Entity @NotNull [] getEntities(); /** * Get a list of all tile entities in the chunk. @@ -143,7 +142,7 @@ public interface Chunk extends PersistentDataHolder { */ @NotNull // Paper start - default BlockState[] getTileEntities() { + default BlockState @NotNull [] getTileEntities() { return getTileEntities(true); } @@ -154,7 +153,7 @@ public interface Chunk extends PersistentDataHolder { * @return The tile entities. */ @NotNull - BlockState[] getTileEntities(boolean useSnapshot); + BlockState @NotNull [] getTileEntities(boolean useSnapshot); /** * Get a list of all tile entities that match a given predicate in the chunk. diff --git a/paper-api/src/main/java/org/bukkit/Registry.java b/paper-api/src/main/java/org/bukkit/Registry.java index d55c33ca1..50eea2967 100644 --- a/paper-api/src/main/java/org/bukkit/Registry.java +++ b/paper-api/src/main/java/org/bukkit/Registry.java @@ -3,13 +3,22 @@ package org.bukkit; import com.google.common.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableMap; +import io.papermc.paper.datacomponent.DataComponentType; +import io.papermc.paper.registry.RegistryAccess; +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 java.util.Locale; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import net.kyori.adventure.key.Key; import org.bukkit.advancement.Advancement; import org.bukkit.attribute.Attribute; import org.bukkit.block.Biome; @@ -35,8 +44,8 @@ import org.bukkit.map.MapCursor; import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionType; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Represents a registry of Bukkit objects that may be retrieved by @@ -44,39 +53,34 @@ import org.jetbrains.annotations.Nullable; * * @param type of item in the registry */ +@NullMarked public interface Registry extends Iterable { + private static Registry registryFor(final RegistryKey registryKey) { + return RegistryAccess.registryAccess().getRegistry(registryKey); + } + + @SuppressWarnings("removal") + @Deprecated(forRemoval = true, since = "1.21.4") + private static Registry legacyRegistryFor(final Class clazz) { + return Objects.requireNonNull(RegistryAccess.registryAccess().getRegistry(clazz), "No registry present for " + clazz.getSimpleName() + ". This is a bug."); + } + /** * Server advancements. * - * @see Bukkit#getAdvancement(org.bukkit.NamespacedKey) + * @see Bukkit#getAdvancement(NamespacedKey) * @see Bukkit#advancementIterator() + * @deprecated use {@link Bukkit#getAdvancement(NamespacedKey)} and {@link Bukkit#advancementIterator()} */ - Registry ADVANCEMENT = new Registry() { + @Deprecated(since = "1.21.4", forRemoval = true) + Registry ADVANCEMENT = new NotARegistry<>() { - @Nullable @Override - public Advancement get(@NotNull NamespacedKey key) { + public @Nullable Advancement get(final NamespacedKey key) { return Bukkit.getAdvancement(key); } - @NotNull - @Override - public Advancement getOrThrow(@NotNull NamespacedKey key) { - Advancement advancement = get(key); - - Preconditions.checkArgument(advancement != null, "No Advancement registry entry found for key %s.", key); - - return advancement; - } - - @NotNull - @Override - public Stream stream() { - return StreamSupport.stream(spliterator(), false); - } - - @NotNull @Override public Iterator iterator() { return Bukkit.advancementIterator(); @@ -86,71 +90,54 @@ public interface Registry extends Iterable { * Server art. * * @see Art - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#PAINTING_VARIANT} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#PAINTING_VARIANT} */ @Deprecated(since = "1.21.3") // Paper - Registry ART = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Art.class), "No registry present for Art. This is a bug."); + Registry ART = legacyRegistryFor(Art.class); /** * Attribute. * * @see Attribute */ - Registry ATTRIBUTE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.ATTRIBUTE); // Paper + Registry ATTRIBUTE = registryFor(RegistryKey.ATTRIBUTE); /** * Server banner patterns. * * @see PatternType - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#BANNER_PATTERN} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#BANNER_PATTERN} */ @Deprecated(since = "1.21") // Paper - Registry BANNER_PATTERN = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(PatternType.class), "No registry present for PatternType. This is a bug."); // Paper + Registry BANNER_PATTERN = legacyRegistryFor(PatternType.class); /** * Server biomes. * * @see Biome - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#BIOME} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#BIOME} */ @Deprecated(since = "1.21.3") // Paper - Registry BIOME = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Biome.class), "No registry present for Biome. This is a bug."); + Registry BIOME = legacyRegistryFor(Biome.class); /** * Server block types. * * @see BlockType - * @apiNote BlockType is not ready for public usage yet */ - @ApiStatus.Internal - Registry BLOCK = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.BLOCK); // Paper + @ApiStatus.Experimental + Registry BLOCK = registryFor(RegistryKey.BLOCK); /** * Custom boss bars. * * @see Bukkit#getBossBar(org.bukkit.NamespacedKey) * @see Bukkit#getBossBars() + * @deprecated use {@link Bukkit#getBossBar(NamespacedKey)} and {@link Bukkit#getBossBars()} */ - Registry BOSS_BARS = new Registry() { + @Deprecated(since = "1.21.4", forRemoval = true) + Registry BOSS_BARS = new NotARegistry<>() { - @Nullable @Override - public KeyedBossBar get(@NotNull NamespacedKey key) { + public @Nullable KeyedBossBar get(final NamespacedKey key) { return Bukkit.getBossBar(key); } - @NotNull - @Override - public KeyedBossBar getOrThrow(@NotNull NamespacedKey key) { - KeyedBossBar keyedBossBar = get(key); - - Preconditions.checkArgument(keyedBossBar != null, "No KeyedBossBar registry entry found for key %s.", key); - - return keyedBossBar; - } - - @NotNull - @Override - public Stream stream() { - return StreamSupport.stream(spliterator(), false); - } - - @NotNull @Override public Iterator iterator() { return Bukkit.getBossBars(); @@ -161,37 +148,36 @@ public interface Registry extends Iterable { * * @see Cat.Type */ - Registry CAT_VARIANT = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.CAT_VARIANT); // Paper + Registry CAT_VARIANT = registryFor(RegistryKey.CAT_VARIANT); /** * Server enchantments. * * @see Enchantment - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#ENCHANTMENT} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#ENCHANTMENT} */ @Deprecated(since = "1.21") - Registry ENCHANTMENT = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Enchantment.class), "No registry present for Enchantment. This is a bug."); // Paper + Registry ENCHANTMENT = legacyRegistryFor(Enchantment.class); /** * Server entity types. * * @see EntityType */ - Registry ENTITY_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.ENTITY_TYPE); // Paper + Registry ENTITY_TYPE = registryFor(RegistryKey.ENTITY_TYPE); /** * Server instruments. * * @see MusicInstrument - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#INSTRUMENT} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#INSTRUMENT} */ @Deprecated(since = "1.21.2") - Registry INSTRUMENT = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(MusicInstrument.class), "No registry present for Instruments. This is a bug."); // Paper + Registry INSTRUMENT = legacyRegistryFor(MusicInstrument.class); /** * Server item types. * * @see ItemType - * @apiNote ItemType is not ready for public usage yet */ - @ApiStatus.Internal - Registry ITEM = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.ITEM); // Paper + @ApiStatus.Experimental + Registry ITEM = registryFor(RegistryKey.ITEM); /** * Default server loot tables. * @@ -210,25 +196,25 @@ public interface Registry extends Iterable { * @see MenuType */ @ApiStatus.Experimental - Registry MENU = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.MENU); // Paper + Registry MENU = registryFor(RegistryKey.MENU); /** * Server mob effects. * * @see PotionEffectType */ - Registry EFFECT = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.MOB_EFFECT); // Paper + Registry MOB_EFFECT = registryFor(RegistryKey.MOB_EFFECT); /** * Server particles. * * @see Particle */ - Registry PARTICLE_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.PARTICLE_TYPE); // Paper + Registry PARTICLE_TYPE = registryFor(RegistryKey.PARTICLE_TYPE); // Paper /** * Server potions. * * @see PotionType */ - Registry POTION = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.POTION); // Paper + Registry POTION = registryFor(RegistryKey.POTION); // Paper /** * Server statistics. * @@ -239,160 +225,161 @@ public interface Registry extends Iterable { * Server structures. * * @see Structure - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#STRUCTURE} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#STRUCTURE} */ @Deprecated(since = "1.20.6") // Paper - Registry STRUCTURE = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Structure.class), "No registry present for Structure. This is a bug."); // Paper + Registry STRUCTURE = legacyRegistryFor(Structure.class); /** * Server structure types. * * @see StructureType */ - Registry STRUCTURE_TYPE = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.STRUCTURE_TYPE), "No registry present for StructureType. This is a bug."); // Paper + Registry STRUCTURE_TYPE = registryFor(RegistryKey.STRUCTURE_TYPE); /** - * Sound keys. + * Sound events. * * @see Sound */ - Registry SOUNDS = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.SOUND_EVENT); // Paper + Registry SOUND_EVENT = registryFor(RegistryKey.SOUND_EVENT); /** * Trim materials. * * @see TrimMaterial - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#TRIM_MATERIAL} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#TRIM_MATERIAL} */ @Deprecated(since = "1.20.6") // Paper - Registry TRIM_MATERIAL = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(TrimMaterial.class), "No registry present for TrimMaterial. This is a bug."); // Paper + Registry TRIM_MATERIAL = legacyRegistryFor(TrimMaterial.class); /** * Trim patterns. * * @see TrimPattern - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#TRIM_PATTERN} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#TRIM_PATTERN} */ @Deprecated(since = "1.20.6") - Registry TRIM_PATTERN = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(TrimPattern.class), "No registry present for TrimPattern. This is a bug."); // Paper + Registry TRIM_PATTERN = legacyRegistryFor(TrimPattern.class); /** * Damage types. * * @see DamageType - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#DAMAGE_TYPE} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#DAMAGE_TYPE} */ @Deprecated(since = "1.20.6") - Registry DAMAGE_TYPE = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(DamageType.class), "No registry present for DamageType. This is a bug."); // Paper + Registry DAMAGE_TYPE = legacyRegistryFor(DamageType.class); /** * Jukebox songs. * * @see JukeboxSong - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#JUKEBOX_SONG} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#JUKEBOX_SONG} */ @ApiStatus.Experimental @Deprecated(since = "1.21") - Registry JUKEBOX_SONG = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(JukeboxSong.class), "No registry present for JukeboxSong. This is a bug."); // Paper + Registry JUKEBOX_SONG = legacyRegistryFor(JukeboxSong.class); /** * Villager profession. * * @see Villager.Profession */ - Registry VILLAGER_PROFESSION = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.VILLAGER_PROFESSION); // Paper + Registry VILLAGER_PROFESSION = registryFor(RegistryKey.VILLAGER_PROFESSION); /** * Villager type. * * @see Villager.Type */ - Registry VILLAGER_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.VILLAGER_TYPE); // Paper + Registry VILLAGER_TYPE = registryFor(RegistryKey.VILLAGER_TYPE); /** * Memory Keys. * * @see MemoryKey */ - Registry MEMORY_MODULE_TYPE = new Registry() { + Registry MEMORY_MODULE_TYPE = new NotARegistry<>() { - @NotNull @Override public Iterator iterator() { return MemoryKey.values().iterator(); } - @Nullable @Override - public MemoryKey get(@NotNull NamespacedKey key) { + public @Nullable MemoryKey get(final NamespacedKey key) { return MemoryKey.getByKey(key); } - - @NotNull - @Override - public MemoryKey getOrThrow(@NotNull NamespacedKey key) { - MemoryKey memoryKey = get(key); - - Preconditions.checkArgument(memoryKey != null, "No MemoryKey registry entry found for key %s.", key); - - return memoryKey; - } - - @NotNull - @Override - public Stream stream() { - return StreamSupport.stream(spliterator(), false); - } }; /** * Server fluids. * * @see Fluid */ - Registry FLUID = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.FLUID); // Paper + Registry FLUID = registryFor(RegistryKey.FLUID); /** * Frog variants. * * @see Frog.Variant */ - Registry FROG_VARIANT = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.FROG_VARIANT); // Paper + Registry FROG_VARIANT = registryFor(RegistryKey.FROG_VARIANT); /** * Wolf variants. * * @see Wolf.Variant - * @deprecated use {@link io.papermc.paper.registry.RegistryAccess#getRegistry(io.papermc.paper.registry.RegistryKey)} with {@link io.papermc.paper.registry.RegistryKey#WOLF_VARIANT} + * @deprecated use {@link RegistryAccess#getRegistry(RegistryKey)} with {@link RegistryKey#WOLF_VARIANT} */ @Deprecated(since = "1.20.6") - Registry WOLF_VARIANT = Objects.requireNonNull(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(Wolf.Variant.class), "No registry present for Wolf$Variant. This is a bug."); // Paper + Registry WOLF_VARIANT = legacyRegistryFor(Wolf.Variant.class); /** * Map cursor types. * * @see MapCursor.Type */ - Registry MAP_DECORATION_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.MAP_DECORATION_TYPE); // Paper + Registry MAP_DECORATION_TYPE = registryFor(RegistryKey.MAP_DECORATION_TYPE); /** * Game events. * * @see GameEvent + * @see io.papermc.paper.registry.event.RegistryEvents#GAME_EVENT */ - Registry GAME_EVENT = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.GAME_EVENT); // Paper + Registry GAME_EVENT = registryFor(RegistryKey.GAME_EVENT); + /** + * Data component types. + * + * @see DataComponentType + */ + Registry DATA_COMPONENT_TYPE = registryFor(RegistryKey.DATA_COMPONENT_TYPE); // Paper - // Paper start - potion effect type registry + // /** - * Potion effect types. - * - * @see org.bukkit.potion.PotionEffectType + * @apiNote use {@link #MOB_EFFECT} instead + * @hidden */ + @ApiStatus.Obsolete(since = "1.21.4") + Registry EFFECT = MOB_EFFECT; + /** + * @apiNote use {@link #MOB_EFFECT} instead + * @hidden + */ + @ApiStatus.Obsolete(since = "1.21.4") Registry POTION_EFFECT_TYPE = EFFECT; - // Paper end - potion effect type registry - Registry DATA_COMPONENT_TYPE = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(io.papermc.paper.registry.RegistryKey.DATA_COMPONENT_TYPE); // Paper /** - * Get the object by its key. - * - * @param key non-null key - * @return item or null if does not exist + * @apiNote use {@link #SOUND_EVENT} + * @hidden */ - @Nullable - T get(@NotNull NamespacedKey key); - // Paper start + @ApiStatus.Obsolete(since = "1.21.4") + Registry SOUNDS = registryFor(RegistryKey.SOUND_EVENT); + // + /** * Get the object by its key. * * @param key non-null key * @return item or null if it does not exist */ - default @Nullable T get(final net.kyori.adventure.key.@NotNull Key key) { + @Nullable T get(NamespacedKey key); + // Paper start + + /** + * Get the object by its key. + * + * @param key non-null key + * @return item or null if it does not exist + */ + default @Nullable T get(final Key key) { return key instanceof final NamespacedKey nsKey ? this.get(nsKey) : this.get(new NamespacedKey(key.namespace(), key.value())); } @@ -402,23 +389,26 @@ public interface Registry extends Iterable { * @param typedKey non-null typed key * @return item or null if it does not exist */ - default @Nullable T get(final io.papermc.paper.registry.@NotNull TypedKey typedKey) { + default @Nullable T get(final TypedKey typedKey) { + Preconditions.checkArgument(typedKey != null, "typedKey cannot be null"); return this.get(typedKey.key()); } // Paper end // Paper start - improve Registry + /** * Gets the object by its key or throws if it doesn't exist. * * @param key the key to get the object of in this registry * @return the object for the key - * @throws java.util.NoSuchElementException if the key doesn't point to an object in the registry + * @throws NoSuchElementException if the key doesn't point to an object in the registry */ - default @NotNull T getOrThrow(final net.kyori.adventure.key.@NotNull Key key) { + default T getOrThrow(final net.kyori.adventure.key.Key key) { + Preconditions.checkArgument(key != null, "key cannot be null"); final T value = this.get(key); if (value == null) { - throw new java.util.NoSuchElementException("No value for " + key + " in " + this); + throw new NoSuchElementException("No value for " + key + " in " + this); } return value; } @@ -428,12 +418,12 @@ public interface Registry extends Iterable { * * @param key the key to get the object of in this registry * @return the object for the key - * @throws java.util.NoSuchElementException if the key doesn't point to an object in the registry + * @throws NoSuchElementException if the key doesn't point to an object in the registry */ - default @NotNull T getOrThrow(final io.papermc.paper.registry.@NotNull TypedKey key) { + default T getOrThrow(final TypedKey key) { final T value = this.get(key); if (value == null) { - throw new java.util.NoSuchElementException("No value for " + key + " in " + this); + throw new NoSuchElementException("No value for " + key + " in " + this); } return value; } @@ -447,14 +437,13 @@ public interface Registry extends Iterable { * * @param value the value to get the key of in this registry * @return the key for the value - * @throws java.util.NoSuchElementException if the value doesn't exist in this registry + * @throws NoSuchElementException if the value doesn't exist in this registry * @see #getKey(Keyed) */ - default @NotNull NamespacedKey getKeyOrThrow(final @NotNull T value) { - Preconditions.checkArgument(value != null, "value cannot be null"); + default NamespacedKey getKeyOrThrow(final T value) { final NamespacedKey key = this.getKey(value); if (key == null) { - throw new java.util.NoSuchElementException(value + " has no key in " + this); + throw new NoSuchElementException(value + " has no key in " + this); } return key; } @@ -470,14 +459,7 @@ public interface Registry extends Iterable { * @return the key for the value or null if not in the registry * @see #getKeyOrThrow(Keyed) */ - default @Nullable NamespacedKey getKey(final @NotNull T value) { - Preconditions.checkArgument(value != null, "value cannot be null"); - //noinspection ConstantValue (it might not be in the future...) - if (value instanceof Keyed) { - return value.getKey(); - } - return null; - } + @Nullable NamespacedKey getKey(T value); // Paper end - improve Registry // Paper start - RegistrySet API @@ -486,46 +468,52 @@ public interface Registry extends Iterable { * * @param key the key to check for * @return true if this registry has a tag with the given key, false otherwise - * @see #getTag(io.papermc.paper.registry.tag.TagKey) + * @throws UnsupportedOperationException if this registry doesn't have or support tags + * @see #getTag(TagKey) */ @ApiStatus.Experimental - default boolean hasTag(final io.papermc.paper.registry.tag.@NotNull TagKey key) { - throw new UnsupportedOperationException(this + " doesn't have tags"); - } + boolean hasTag(TagKey key); /** * Gets the named registry set (tag) for the given key. * * @param key the key to get the tag for * @return the tag for the key - * @throws java.util.NoSuchElementException if no tag with the given key is found - * @throws UnsupportedOperationException if this registry doesn't have or support tags - * @see #hasTag(io.papermc.paper.registry.tag.TagKey) + * @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) */ @ApiStatus.Experimental - default @NotNull io.papermc.paper.registry.tag.Tag getTag(final io.papermc.paper.registry.tag.@NotNull TagKey key) { - throw new UnsupportedOperationException(this + " doesn't have tags"); - } + Tag getTag(TagKey key); + + /** + * Gets all the tags in this registry. + * + * @return a stream of all tags in this registry + * @throws UnsupportedOperationException if this registry doesn't have or support tags + */ + @ApiStatus.Experimental + Collection> getTags(); // Paper end - RegistrySet API /** * Get the object by its key. - * + *

* If there is no object with the given key, an exception will be thrown. * * @param key to get the object from * @return object with the given key - * @throws IllegalArgumentException if there is no object with the given key + * @throws NoSuchElementException if there is no object with the given key */ - @NotNull - T getOrThrow(@NotNull NamespacedKey key); + default T getOrThrow(final NamespacedKey key) { + return this.getOrThrow((Key) key); + } /** * Returns a new stream, which contains all registry items, which are registered to the registry. * * @return a stream of all registry items */ - @NotNull Stream stream(); /** @@ -539,64 +527,47 @@ public interface Registry extends Iterable { * @deprecated this method's behavior is broken and not useful. If you want to get an object * based on its vanilla name, or a key, wrap it in a {@link NamespacedKey} object and use {@link #get(NamespacedKey)} */ - @Nullable - @Deprecated(forRemoval = true) // Paper - default T match(@NotNull String input) { + // Paper + @Deprecated(forRemoval = true) + default @Nullable T match(final String input) { Preconditions.checkArgument(input != null, "input must not be null"); - String filtered = input.toLowerCase(Locale.ROOT).replaceAll("\\s+", "_"); - NamespacedKey namespacedKey = NamespacedKey.fromString(filtered); - return (namespacedKey != null) ? get(namespacedKey) : null; + final String filtered = input.toLowerCase(Locale.ROOT).replaceAll("\\s+", "_"); + final NamespacedKey namespacedKey = NamespacedKey.fromString(filtered); + return (namespacedKey != null) ? this.get(namespacedKey) : null; } - class SimpleRegistry & Keyed> implements Registry { // Paper - remove final + @ApiStatus.Internal + class SimpleRegistry & Keyed> extends NotARegistry { // Paper - remove final private final Class type; private final Map map; - protected SimpleRegistry(@NotNull Class type) { - this(type, Predicates.alwaysTrue()); + protected SimpleRegistry(final Class type) { + this(type, Predicates.alwaysTrue()); } - protected SimpleRegistry(@NotNull Class type, @NotNull Predicate predicate) { - ImmutableMap.Builder builder = ImmutableMap.builder(); + protected SimpleRegistry(final Class type, final Predicate predicate) { + final ImmutableMap.Builder builder = ImmutableMap.builder(); - for (T entry : type.getEnumConstants()) { + for (final T entry : type.getEnumConstants()) { if (predicate.test(entry)) { builder.put(entry.getKey(), entry); } } - map = builder.build(); + this.map = builder.build(); this.type = type; } - @Nullable @Override - public T get(@NotNull NamespacedKey key) { - return map.get(key); + public @Nullable T get(final NamespacedKey key) { + return this.map.get(key); } - @NotNull - @Override - public T getOrThrow(@NotNull NamespacedKey key) { - T object = get(key); - - Preconditions.checkArgument(object != null, "No %s registry entry found for key %s.", type, key); - - return object; - } - - @NotNull - @Override - public Stream stream() { - return StreamSupport.stream(spliterator(), false); - } - - @NotNull @Override public Iterator iterator() { - return map.values().iterator(); + return this.map.values().iterator(); } @ApiStatus.Internal @@ -604,12 +575,34 @@ public interface Registry extends Iterable { public Class getType() { return this.type; } + } + + @ApiStatus.Internal + abstract class NotARegistry implements Registry { - // Paper start - improve Registry @Override - public @NotNull NamespacedKey getKey(final @NotNull T value) { + public Stream stream() { + return StreamSupport.stream(this.spliterator(), false); + } + + @Override + public NamespacedKey getKey(final A value) { return value.getKey(); } - // Paper end - improve Registry + + @Override + public boolean hasTag(final TagKey key) { + throw new UnsupportedOperationException("This is not a real registry and therefore cannot support tags"); + } + + @Override + public Tag getTag(final TagKey key) { + throw new UnsupportedOperationException("This is not a real registry and therefore cannot support tags"); + } + + @Override + public Collection> getTags() { + throw new UnsupportedOperationException("This is not a real registry and therefore cannot support tags"); + } } } diff --git a/paper-api/src/main/java/org/bukkit/Server.java b/paper-api/src/main/java/org/bukkit/Server.java index ad816538b..e47e482d7 100644 --- a/paper-api/src/main/java/org/bukkit/Server.java +++ b/paper-api/src/main/java/org/bukkit/Server.java @@ -1018,7 +1018,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi * @return the {@link Recipe} resulting from the given crafting matrix. */ @Nullable - public Recipe getCraftingRecipe(@NotNull ItemStack[] craftingMatrix, @NotNull World world); + public Recipe getCraftingRecipe(@NotNull ItemStack @NotNull [] craftingMatrix, @NotNull World world); /** * Get the crafted item using the list of {@link ItemStack} provided. @@ -1046,7 +1046,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi * an ItemStack of {@link Material#AIR} is returned. */ @NotNull - public ItemStack craftItem(@NotNull ItemStack[] craftingMatrix, @NotNull World world, @NotNull Player player); + public ItemStack craftItem(@NotNull ItemStack @NotNull [] craftingMatrix, @NotNull World world, @NotNull Player player); /** * Get the crafted item using the list of {@link ItemStack} provided. @@ -1067,7 +1067,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi * an ItemStack of {@link Material#AIR} is returned. */ @NotNull - public ItemStack craftItem(@NotNull ItemStack[] craftingMatrix, @NotNull World world); + public ItemStack craftItem(@NotNull ItemStack @NotNull [] craftingMatrix, @NotNull World world); /** * Get the crafted item using the list of {@link ItemStack} provided. @@ -1094,7 +1094,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi * @return resulting {@link ItemCraftResult} containing the resulting item, matrix and any overflow items. */ @NotNull - public ItemCraftResult craftItemResult(@NotNull ItemStack[] craftingMatrix, @NotNull World world, @NotNull Player player); + public ItemCraftResult craftItemResult(@NotNull ItemStack @NotNull [] craftingMatrix, @NotNull World world, @NotNull Player player); /** * Get the crafted item using the list of {@link ItemStack} provided. @@ -1114,7 +1114,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi * @return resulting {@link ItemCraftResult} containing the resulting item, matrix and any overflow items. */ @NotNull - public ItemCraftResult craftItemResult(@NotNull ItemStack[] craftingMatrix, @NotNull World world); + public ItemCraftResult craftItemResult(@NotNull ItemStack @NotNull [] craftingMatrix, @NotNull World world); /** * Get an iterator through the list of crafting recipes. @@ -1503,8 +1503,7 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi * * @return an array containing all previous players */ - @NotNull - public OfflinePlayer[] getOfflinePlayers(); + public @NotNull OfflinePlayer @NotNull [] getOfflinePlayers(); /** * Gets the {@link Messenger} responsible for this server. @@ -2057,16 +2056,14 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi * * @return current server TPS (1m, 5m, 15m in Paper-Server) */ - @NotNull - public double[] getTPS(); + public double @NotNull [] getTPS(); /** * Get a sample of the servers last tick times (in nanos) * * @return A sample of the servers last tick times (in nanos) */ - @NotNull - long[] getTickTimes(); + long @NotNull [] getTickTimes(); /** * Get the average tick time (in millis) diff --git a/paper-api/src/main/java/org/bukkit/Tag.java b/paper-api/src/main/java/org/bukkit/Tag.java index 961a36e03..193858a28 100644 --- a/paper-api/src/main/java/org/bukkit/Tag.java +++ b/paper-api/src/main/java/org/bukkit/Tag.java @@ -1050,9 +1050,16 @@ public interface Tag extends Keyed { */ Tag ITEMS_REPAIRS_WOLF_ARMOR = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("repairs_wolf_armor"), Material.class); /** - * Vanilla item tag representing all furnace materials. + * Vanilla item tag representing all stone based materials for crafting. */ - Tag ITEMS_FURNACE_MATERIALS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("furnace_materials"), Material.class); + Tag ITEMS_STONE_CRAFTING_MATERIALS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("stone_crafting_materials"), Material.class); + /** + * Vanilla item tag representing all furnace materials. + * + * @deprecated partially replaced by {@link #ITEMS_STONE_CRAFTING_MATERIALS} + */ + @Deprecated(since = "1.16.2", forRemoval = true) + Tag ITEMS_FURNACE_MATERIALS = ITEMS_STONE_CRAFTING_MATERIALS; /** * Vanilla item tag representing all compasses. */ diff --git a/paper-api/src/main/java/org/bukkit/UnsafeValues.java b/paper-api/src/main/java/org/bukkit/UnsafeValues.java index d0de7ce3c..29838175a 100644 --- a/paper-api/src/main/java/org/bukkit/UnsafeValues.java +++ b/paper-api/src/main/java/org/bukkit/UnsafeValues.java @@ -1,6 +1,7 @@ package org.bukkit; import com.google.common.collect.Multimap; +import io.papermc.paper.entity.EntitySerializationFlag; import org.bukkit.advancement.Advancement; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeModifier; @@ -9,7 +10,9 @@ import org.bukkit.block.data.BlockData; import org.bukkit.damage.DamageEffect; import org.bukkit.damage.DamageSource; import org.bukkit.damage.DamageType; +import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.inventory.CreativeCategory; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; @@ -123,10 +126,6 @@ public interface UnsafeValues { @Deprecated(since = "1.20.2", forRemoval = true) PotionType.InternalPotionData getInternalPotionData(NamespacedKey key); - @ApiStatus.Internal - @Nullable - DamageEffect getDamageEffect(@NotNull String key); - /** * Create a new {@link DamageSource.Builder}. * @@ -198,13 +197,81 @@ public interface UnsafeValues { */ @NotNull ItemStack deserializeItemFromJson(@NotNull com.google.gson.JsonObject data) throws IllegalArgumentException; - byte[] serializeEntity(org.bukkit.entity.Entity entity); + /** + * Serializes the provided entity. + * + * @param entity entity + * @return serialized entity data + * @see #serializeEntity(Entity, EntitySerializationFlag...) + * @see #deserializeEntity(byte[], World, boolean, boolean) + * @throws IllegalArgumentException if couldn't serialize the entity + * @since 1.17.1 + */ + default byte @NotNull [] serializeEntity(@NotNull Entity entity) { + return serializeEntity(entity, new EntitySerializationFlag[0]); + } - default org.bukkit.entity.Entity deserializeEntity(byte[] data, World world) { + /** + * Serializes the provided entity. + * + * @param entity entity + * @param serializationFlags serialization flags + * @return serialized entity data + * @throws IllegalArgumentException if couldn't serialize the entity + * @see #deserializeEntity(byte[], World, boolean, boolean) + * @since 1.21.4 + */ + byte @NotNull [] serializeEntity(@NotNull Entity entity, @NotNull EntitySerializationFlag... serializationFlags); + + /** + * Deserializes the entity from data. + *
The entity's {@link java.util.UUID} as well as passengers will not be preserved. + * + * @param data serialized entity data + * @param world world + * @return deserialized entity + * @throws IllegalArgumentException if invalid serialized entity data provided + * @see #deserializeEntity(byte[], World, boolean, boolean) + * @see #serializeEntity(Entity, EntitySerializationFlag...) + * @see Entity#spawnAt(Location, CreatureSpawnEvent.SpawnReason) + * @since 1.17.1 + */ + default @NotNull Entity deserializeEntity(byte @NotNull [] data, @NotNull World world) { return deserializeEntity(data, world, false); } - org.bukkit.entity.Entity deserializeEntity(byte[] data, World world, boolean preserveUUID); + /** + * Deserializes the entity from data. + *
The entity's passengers will not be preserved. + * + * @param data serialized entity data + * @param world world + * @param preserveUUID whether to preserve the entity's uuid + * @return deserialized entity + * @throws IllegalArgumentException if invalid serialized entity data provided + * @see #deserializeEntity(byte[], World, boolean, boolean) + * @see #serializeEntity(Entity, EntitySerializationFlag...) + * @see Entity#spawnAt(Location, CreatureSpawnEvent.SpawnReason) + * @since 1.17.1 + */ + default @NotNull Entity deserializeEntity(byte @NotNull [] data, @NotNull World world, boolean preserveUUID) { + return deserializeEntity(data, world, preserveUUID, false); + } + + /** + * Deserializes the entity from data. + * + * @param data serialized entity data + * @param world world + * @param preserveUUID whether to preserve uuids of the entity and its passengers + * @param preservePassengers whether to preserve passengers + * @return deserialized entity + * @throws IllegalArgumentException if invalid serialized entity data provided + * @see #serializeEntity(Entity, EntitySerializationFlag...) + * @see Entity#spawnAt(Location, CreatureSpawnEvent.SpawnReason) + * @since 1.21.4 + */ + @NotNull Entity deserializeEntity(byte @NotNull [] data, @NotNull World world, boolean preserveUUID, boolean preservePassengers); /** * Creates and returns the next EntityId available. diff --git a/paper-api/src/main/java/org/bukkit/World.java b/paper-api/src/main/java/org/bukkit/World.java index bef54a6c8..e5e6cdec5 100644 --- a/paper-api/src/main/java/org/bukkit/World.java +++ b/paper-api/src/main/java/org/bukkit/World.java @@ -346,8 +346,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * * @return Chunk[] containing all loaded chunks */ - @NotNull - public Chunk[] getLoadedChunks(); + public @NotNull Chunk @NotNull [] getLoadedChunks(); /** * Loads the specified {@link Chunk}. @@ -3563,8 +3562,7 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient * * @return An array of {@link GameRule} names. */ - @NotNull - public String[] getGameRules(); + public @NotNull String @NotNull [] getGameRules(); /** * Gets the current state of the specified rule diff --git a/paper-api/src/main/java/org/bukkit/attribute/Attribute.java b/paper-api/src/main/java/org/bukkit/attribute/Attribute.java index 521f03540..273ef25fe 100644 --- a/paper-api/src/main/java/org/bukkit/attribute/Attribute.java +++ b/paper-api/src/main/java/org/bukkit/attribute/Attribute.java @@ -89,7 +89,7 @@ public interface Attribute extends OldEnum, Keyed, Translatable, net. */ Attribute JUMP_STRENGTH = getAttribute("jump_strength"); /** - * How long an entity remains burning after ingition. + * How long an entity remains burning after ignition. */ Attribute BURNING_TIME = getAttribute("burning_time"); /** diff --git a/paper-api/src/main/java/org/bukkit/command/Command.java b/paper-api/src/main/java/org/bukkit/command/Command.java index 74384a56e..71eb845a4 100644 --- a/paper-api/src/main/java/org/bukkit/command/Command.java +++ b/paper-api/src/main/java/org/bukkit/command/Command.java @@ -66,7 +66,7 @@ public abstract class Command { * @param args All arguments passed to the command, split via ' ' * @return true if the command was successful, otherwise false */ - public abstract boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args); + public abstract boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String @NotNull [] args); /** * Executed on tab completion for this command, returning a list of @@ -80,7 +80,7 @@ public abstract class Command { * @throws IllegalArgumentException if sender, alias, or args is null */ @NotNull - public List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException { + public List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String @NotNull [] args) throws IllegalArgumentException { return tabComplete0(sender, alias, args, null); } @@ -97,7 +97,7 @@ public abstract class Command { * @throws IllegalArgumentException if sender, alias, or args is null */ @NotNull - public List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args, @Nullable Location location) throws IllegalArgumentException { + public List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String @NotNull [] args, @Nullable Location location) throws IllegalArgumentException { return tabComplete(sender, alias, args); } diff --git a/paper-api/src/main/java/org/bukkit/command/CommandExecutor.java b/paper-api/src/main/java/org/bukkit/command/CommandExecutor.java index 45cb8da12..8d7139467 100644 --- a/paper-api/src/main/java/org/bukkit/command/CommandExecutor.java +++ b/paper-api/src/main/java/org/bukkit/command/CommandExecutor.java @@ -19,5 +19,5 @@ public interface CommandExecutor { * @param args Passed command arguments * @return true if a valid command, otherwise false */ - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args); + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args); } diff --git a/paper-api/src/main/java/org/bukkit/command/MessageCommandSender.java b/paper-api/src/main/java/org/bukkit/command/MessageCommandSender.java index 946d2f0ef..d9137e2d3 100644 --- a/paper-api/src/main/java/org/bukkit/command/MessageCommandSender.java +++ b/paper-api/src/main/java/org/bukkit/command/MessageCommandSender.java @@ -22,7 +22,7 @@ import org.jetbrains.annotations.Nullable; public interface MessageCommandSender extends CommandSender { @Override - default void sendMessage(@NotNull String[] messages) { + default void sendMessage(@NotNull String @NotNull [] messages) { for (String message : messages) { sendMessage(message); } @@ -34,7 +34,7 @@ public interface MessageCommandSender extends CommandSender { } @Override - default void sendMessage(@Nullable UUID sender, @NotNull String[] messages) { + default void sendMessage(@Nullable UUID sender, @NotNull String @NotNull [] messages) { for (String message : messages) { sendMessage(message); } diff --git a/paper-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java b/paper-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java index 8487bfe33..9a47b364d 100644 --- a/paper-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java +++ b/paper-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java @@ -18,8 +18,7 @@ public class MultipleCommandAlias extends Command { * * @return commands associated with alias */ - @NotNull - public Command[] getCommands() { + public @NotNull Command @NotNull [] getCommands() { return commands; } diff --git a/paper-api/src/main/java/org/bukkit/command/TabCompleter.java b/paper-api/src/main/java/org/bukkit/command/TabCompleter.java index ed6f6525a..2bcdaca5f 100644 --- a/paper-api/src/main/java/org/bukkit/command/TabCompleter.java +++ b/paper-api/src/main/java/org/bukkit/command/TabCompleter.java @@ -23,5 +23,5 @@ public interface TabCompleter { * to default to the command executor */ @Nullable - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args); + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args); } diff --git a/paper-api/src/main/java/org/bukkit/damage/DamageEffect.java b/paper-api/src/main/java/org/bukkit/damage/DamageEffect.java index 8cf8fde60..2e442f146 100644 --- a/paper-api/src/main/java/org/bukkit/damage/DamageEffect.java +++ b/paper-api/src/main/java/org/bukkit/damage/DamageEffect.java @@ -1,7 +1,7 @@ package org.bukkit.damage; import com.google.common.base.Preconditions; -import org.bukkit.Bukkit; +import io.papermc.paper.InternalAPIBridge; import org.bukkit.Sound; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -40,7 +40,7 @@ public interface DamageEffect { @NotNull private static DamageEffect getDamageEffect(@NotNull String key) { - return Preconditions.checkNotNull(Bukkit.getUnsafe().getDamageEffect(key), "No DamageEffect found for %s. This is a bug.", key); + return Preconditions.checkNotNull(InternalAPIBridge.get().getDamageEffect(key), "No DamageEffect found for %s. This is a bug.", key); } /** diff --git a/paper-api/src/main/java/org/bukkit/entity/Entity.java b/paper-api/src/main/java/org/bukkit/entity/Entity.java index 3fe738c0d..ddf7829ee 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Entity.java +++ b/paper-api/src/main/java/org/bukkit/entity/Entity.java @@ -14,6 +14,7 @@ import org.bukkit.World; import org.bukkit.block.BlockFace; import org.bukkit.block.PistonMoveReaction; import org.bukkit.command.CommandSender; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.material.Directional; @@ -1072,11 +1073,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent *

* Also, this method will fire the same events as a normal entity spawn. * - * @param location The location to spawn the entity at. - * @return Whether the entity was successfully spawned. + * @param location the location to spawn the entity at + * @return whether the entity was successfully spawned + * @since 1.17.1 */ - public default boolean spawnAt(@NotNull Location location) { - return spawnAt(location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + default boolean spawnAt(@NotNull Location location) { + return spawnAt(location, CreatureSpawnEvent.SpawnReason.DEFAULT); } /** @@ -1086,11 +1088,12 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent *

* Also, this method will fire the same events as a normal entity spawn. * - * @param location The location to spawn the entity at. - * @param reason The reason for the entity being spawned. - * @return Whether the entity was successfully spawned. + * @param location the location to spawn the entity at + * @param reason the reason for the entity being spawned + * @return whether the entity was successfully spawned + * @since 1.17.1 */ - public boolean spawnAt(@NotNull Location location, @NotNull org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason); + boolean spawnAt(@NotNull Location location, @NotNull CreatureSpawnEvent.SpawnReason reason); /** * Check if entity is inside powdered snow. diff --git a/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java b/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java index 188c8e27d..6303c68b2 100644 --- a/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java +++ b/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java @@ -2,10 +2,12 @@ package org.bukkit.entity; import java.util.Collection; import java.util.Set; +import java.util.function.Consumer; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryView; @@ -14,6 +16,7 @@ import org.bukkit.inventory.MainHand; import org.bukkit.inventory.Merchant; import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.meta.FireworkMeta; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -703,8 +706,115 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder * * @param dropAll True to drop entire stack, false to drop 1 of the stack * @return True if item was dropped successfully + * @apiNote You should instead use {@link #dropItem(EquipmentSlot, int)} or {@link #dropItem(EquipmentSlot)} with a {@link EquipmentSlot#HAND} parameter. */ - public boolean dropItem(boolean dropAll); + @ApiStatus.Obsolete(since = "1.21.4") + boolean dropItem(boolean dropAll); + + /** + * Makes the player drop all items from their inventory based on the inventory slot. + * + * @param slot the equipment slot to drop + * @return the dropped item entity, or null if the action was unsuccessful + */ + @Nullable + default Item dropItem(final int slot) { + return this.dropItem(slot, Integer.MAX_VALUE); + } + + /** + * Makes the player drop an item from their inventory based on the inventory slot. + * + * @param slot the slot to drop + * @param amount the number of items to drop from this slot. Values below one always return null + * @return the dropped item entity, or null if the action was unsuccessful + * @throws IllegalArgumentException if the slot is negative or bigger than the player's inventory + */ + @Nullable + default Item dropItem(final int slot, final int amount) { + return this.dropItem(slot, amount, false, null); + } + + /** + * Makes the player drop an item from their inventory based on the inventory slot. + * + * @param slot the slot to drop + * @param amount the number of items to drop from this slot. Values below one always return null + * @param throwRandomly controls the randomness of the dropped items velocity, where {@code true} mimics dropped + * items during a player's death, while {@code false} acts like a normal item drop. + * @param entityOperation the function to be run before adding the entity into the world + * @return the dropped item entity, or null if the action was unsuccessful + * @throws IllegalArgumentException if the slot is negative or bigger than the player's inventory + */ + @Nullable + Item dropItem(int slot, int amount, boolean throwRandomly, @Nullable Consumer entityOperation); + + /** + * Makes the player drop all items from their inventory based on the equipment slot. + * + * @param slot the equipment slot to drop + * @return the dropped item entity, or null if the action was unsuccessful + */ + @Nullable + default Item dropItem(final @NotNull EquipmentSlot slot) { + return this.dropItem(slot, Integer.MAX_VALUE); + } + + /** + * Makes the player drop an item from their inventory based on the equipment slot. + * + * @param slot the equipment slot to drop + * @param amount the amount of items to drop from this equipment slot. Values below one always return null + * @return the dropped item entity, or null if the action was unsuccessful + */ + @Nullable + default Item dropItem(final @NotNull EquipmentSlot slot, final int amount) { + return this.dropItem(slot, amount, false, null); + } + + /** + * Makes the player drop an item from their inventory based on the equipment slot. + * + * @param slot the equipment slot to drop + * @param amount The amount of items to drop from this equipment slot. Values below one always return null + * @param throwRandomly controls the randomness of the dropped items velocity, where {@code true} mimics dropped + * items during a player's death, while {@code false} acts like a normal item drop. + * @param entityOperation the function to be run before adding the entity into the world + * @return the dropped item entity, or null if the action was unsuccessful + */ + @Nullable + Item dropItem(@NotNull EquipmentSlot slot, int amount, boolean throwRandomly, @Nullable Consumer entityOperation); + + /** + * Makes the player drop any arbitrary {@link ItemStack}, independently of whether the player actually + * has that item in their inventory. + *

+ * This method modifies neither the item nor the player's inventory. + * Item removal has to be handled by the method caller. + * + * @param itemStack the itemstack to drop + * @return the dropped item entity, or null if the action was unsuccessful + */ + @Nullable + default Item dropItem(final @NotNull ItemStack itemStack) { + return this.dropItem(itemStack, false, null); + } + + /** + * Makes the player drop any arbitrary {@link ItemStack}, independently of whether the player actually + * has that item in their inventory. + *

+ * This method modifies neither the item nor the player's inventory. + * Item removal has to be handled by the method caller. + * + * @param itemStack the itemstack to drop + * @param throwRandomly controls the randomness of the dropped items velocity, where {@code true} mimics dropped + * items during a player's death, while {@code false} acts like a normal item drop. + * @param entityOperation the function to be run before adding the entity into the world + * @return the dropped item entity, or null if the action was unsuccessful + */ + @Nullable + Item dropItem(final @NotNull ItemStack itemStack, boolean throwRandomly, @Nullable Consumer entityOperation); /** * Gets the players current exhaustion level. diff --git a/paper-api/src/main/java/org/bukkit/entity/Player.java b/paper-api/src/main/java/org/bukkit/entity/Player.java index be9bea56a..7d21ee64c 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Player.java +++ b/paper-api/src/main/java/org/bukkit/entity/Player.java @@ -2308,7 +2308,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM * long. */ @Deprecated // Paper - adventure - public void setResourcePack(@NotNull String url, @Nullable byte[] hash); + public void setResourcePack(@NotNull String url, byte @Nullable [] hash); /** * Request that the player's client download and switch resource packs. @@ -2353,7 +2353,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM * long. */ @Deprecated // Paper - adventure - public void setResourcePack(@NotNull String url, @Nullable byte[] hash, @Nullable String prompt); + public void setResourcePack(@NotNull String url, byte @Nullable [] hash, @Nullable String prompt); // Paper start /** @@ -2447,7 +2447,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)} */ @Deprecated // Paper - adventure - public void setResourcePack(@NotNull String url, @Nullable byte[] hash, boolean force); + public void setResourcePack(@NotNull String url, byte @Nullable [] hash, boolean force); /** * Request that the player's client download and switch resource packs. @@ -2494,7 +2494,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)} */ @Deprecated // Paper - public void setResourcePack(@NotNull String url, @Nullable byte[] hash, @Nullable String prompt, boolean force); + public void setResourcePack(@NotNull String url, byte @Nullable [] hash, @Nullable String prompt, boolean force); // Paper start /** @@ -2592,7 +2592,7 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)} */ @Deprecated // Paper - adventure - public void setResourcePack(@NotNull UUID id, @NotNull String url, @Nullable byte[] hash, @Nullable String prompt, boolean force); + public void setResourcePack(@NotNull UUID id, @NotNull String url, byte @Nullable [] hash, @Nullable String prompt, boolean force); // Paper start /** diff --git a/paper-api/src/main/java/org/bukkit/event/HandlerList.java b/paper-api/src/main/java/org/bukkit/event/HandlerList.java index 2292bd460..64d8916a8 100644 --- a/paper-api/src/main/java/org/bukkit/event/HandlerList.java +++ b/paper-api/src/main/java/org/bukkit/event/HandlerList.java @@ -203,8 +203,7 @@ public class HandlerList { * * @return the array of registered listeners */ - @NotNull - public RegisteredListener[] getRegisteredListeners() { + public @NotNull RegisteredListener @NotNull [] getRegisteredListeners() { RegisteredListener[] handlers; while ((handlers = this.handlers) == null) bake(); // This prevents fringe cases of returning null return handlers; diff --git a/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java b/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java index 5e52f4dc2..759aaa935 100644 --- a/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/player/PlayerInteractEvent.java @@ -241,6 +241,9 @@ public class PlayerInteractEvent extends PlayerEvent implements Cancellable { @Nullable @Deprecated // Paper public Vector getClickedPosition() { + if (this.clickedPosistion == null) { + return null; + } return clickedPosistion.clone(); } diff --git a/paper-api/src/main/java/org/bukkit/inventory/EntityEquipment.java b/paper-api/src/main/java/org/bukkit/inventory/EntityEquipment.java index 4264cf878..b81e853ad 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/EntityEquipment.java +++ b/paper-api/src/main/java/org/bukkit/inventory/EntityEquipment.java @@ -329,7 +329,7 @@ public interface EntityEquipment { * * @param items The items to set the armor as. Individual items may be null. */ - void setArmorContents(@NotNull ItemStack[] items); + void setArmorContents(@NotNull ItemStack @NotNull [] items); /** * Clears the entity of all armor and held items diff --git a/paper-api/src/main/java/org/bukkit/inventory/ItemCraftResult.java b/paper-api/src/main/java/org/bukkit/inventory/ItemCraftResult.java index ebb4d19e0..74d4785bd 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/ItemCraftResult.java +++ b/paper-api/src/main/java/org/bukkit/inventory/ItemCraftResult.java @@ -24,8 +24,7 @@ public interface ItemCraftResult { * * @return resulting matrix */ - @NotNull - public ItemStack[] getResultingMatrix(); + public @NotNull ItemStack @NotNull [] getResultingMatrix(); /** * Gets the overflowed items for items that don't fit back into the crafting diff --git a/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java b/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java index 8c9654cd1..d95d0d6a8 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java +++ b/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableMap; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; +import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; @@ -710,6 +711,17 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat return Bukkit.getServer().getItemFactory().displayName(this); } + /** + * Gets the effective name of this item stack shown to player in inventory. + * It takes into account the display name (with italics) from the item meta, + * the potion effect, translatable name, rarity etc. + * + * @return the effective name of this item stack + */ + public @NotNull Component effectiveName() { + return this.craftDelegate.effectiveName(); + } + /** * Minecraft updates are converting simple item stacks into more complex NBT oriented Item Stacks. * @@ -732,8 +744,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat * @param bytes bytes representing an item in NBT * @return ItemStack migrated to this version of Minecraft if needed. */ - @NotNull - public static ItemStack deserializeBytes(@NotNull byte[] bytes) { + public static @NotNull ItemStack deserializeBytes(final byte @NotNull [] bytes) { return org.bukkit.Bukkit.getUnsafe().deserializeItem(bytes); } @@ -742,8 +753,7 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat * use the built in data converter instead of bukkits dangerous serialization system. * @return bytes representing this item in NBT. */ - @NotNull - public byte[] serializeAsBytes() { + public byte @NotNull [] serializeAsBytes() { return org.bukkit.Bukkit.getUnsafe().serializeItem(this); } diff --git a/paper-api/src/main/java/org/bukkit/inventory/PlayerInventory.java b/paper-api/src/main/java/org/bukkit/inventory/PlayerInventory.java index 58fcfaf2e..c31f1cebf 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/PlayerInventory.java +++ b/paper-api/src/main/java/org/bukkit/inventory/PlayerInventory.java @@ -118,7 +118,7 @@ public interface PlayerInventory extends Inventory { * * @param items The ItemStacks to use as armour */ - public void setArmorContents(@Nullable ItemStack[] items); + public void setArmorContents(@Nullable ItemStack @NotNull [] items); /** * Put the given ItemStacks into the extra slots @@ -127,7 +127,7 @@ public interface PlayerInventory extends Inventory { * * @param items The ItemStacks to use as extra */ - public void setExtraContents(@Nullable ItemStack[] items); + public void setExtraContents(@Nullable ItemStack @NotNull [] items); /** * Put the given ItemStack into the helmet slot. This does not check if diff --git a/paper-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java b/paper-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java index f0268829c..462a6d1da 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java +++ b/paper-api/src/main/java/org/bukkit/inventory/ShapedRecipe.java @@ -234,8 +234,7 @@ public class ShapedRecipe extends CraftingRecipe { * @return The recipe's shape. * @throws NullPointerException when not set yet */ - @NotNull - public String[] getShape() { + public @NotNull String @NotNull [] getShape() { return rows.clone(); } } diff --git a/paper-api/src/main/java/org/bukkit/inventory/view/EnchantmentView.java b/paper-api/src/main/java/org/bukkit/inventory/view/EnchantmentView.java index 73740c2f5..b82b70c9f 100644 --- a/paper-api/src/main/java/org/bukkit/inventory/view/EnchantmentView.java +++ b/paper-api/src/main/java/org/bukkit/inventory/view/EnchantmentView.java @@ -4,6 +4,7 @@ import org.bukkit.enchantments.EnchantmentOffer; import org.bukkit.inventory.EnchantingInventory; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; /** * An instance of {@link InventoryView} which provides extra methods related to @@ -36,8 +37,7 @@ public interface EnchantmentView extends InventoryView { * * @return The enchantment offers that are provided */ - @NotNull - EnchantmentOffer[] getOffers(); + @Nullable EnchantmentOffer @NotNull [] getOffers(); /** * Sets the offers to provide to the player. @@ -45,5 +45,5 @@ public interface EnchantmentView extends InventoryView { * @param offers The offers to provide * @throws IllegalArgumentException if the array length isn't 3 */ - void setOffers(@NotNull EnchantmentOffer[] offers) throws IllegalArgumentException; + void setOffers(@Nullable EnchantmentOffer @NotNull [] offers) throws IllegalArgumentException; } diff --git a/paper-api/src/main/java/org/bukkit/loot/Lootable.java b/paper-api/src/main/java/org/bukkit/loot/Lootable.java index 649dd9590..4fe569609 100644 --- a/paper-api/src/main/java/org/bukkit/loot/Lootable.java +++ b/paper-api/src/main/java/org/bukkit/loot/Lootable.java @@ -16,7 +16,7 @@ public interface Lootable { /** * Set the loot table for a container or entity. *
- * To remove a loot table use null. + * If the provided loot table is null, the loot table will be reset to its default behavior. * * @param table the Loot Table this {@link org.bukkit.block.Container} or * {@link org.bukkit.entity.Mob} will have. @@ -38,6 +38,8 @@ public interface Lootable { // Paper start /** * Set the loot table and seed for a container or entity at the same time. + *
+ * If the provided loot table is null, the loot table will be reset to its default behavior. * * @param table the Loot Table this {@link org.bukkit.block.Container} or {@link org.bukkit.entity.Mob} will have. * @param seed the seed to used to generate loot. Default is 0. @@ -53,7 +55,8 @@ public interface Lootable { } /** - * Clears the associated Loot Table to this object + * Clears the associated Loot Table to this object, essentially resetting it to default + * @see #setLootTable(LootTable) */ default void clearLootTable() { this.setLootTable(null); diff --git a/paper-api/src/main/java/org/bukkit/map/MapFont.java b/paper-api/src/main/java/org/bukkit/map/MapFont.java index a45ce8198..0f17a1750 100644 --- a/paper-api/src/main/java/org/bukkit/map/MapFont.java +++ b/paper-api/src/main/java/org/bukkit/map/MapFont.java @@ -112,7 +112,7 @@ public class MapFont { private final int height; private final boolean[] data; - public CharacterSprite(int width, int height, @NotNull boolean[] data) { + public CharacterSprite(int width, int height, boolean @NotNull [] data) { this.width = width; this.height = height; this.data = data; diff --git a/paper-api/src/main/java/org/bukkit/plugin/EventExecutor.java b/paper-api/src/main/java/org/bukkit/plugin/EventExecutor.java index 60e086be7..187a127c4 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/EventExecutor.java +++ b/paper-api/src/main/java/org/bukkit/plugin/EventExecutor.java @@ -1,24 +1,15 @@ package org.bukkit.plugin; +import com.google.common.base.Preconditions; +import io.papermc.paper.event.executor.EventExecutorFactory; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import org.bukkit.event.Event; import org.bukkit.event.EventException; import org.bukkit.event.Listener; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; -// Paper start -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; - -import com.destroystokyo.paper.event.executor.MethodHandleEventExecutor; -import com.destroystokyo.paper.event.executor.StaticMethodHandleEventExecutor; -import com.destroystokyo.paper.event.executor.asm.ASMEventExecutorGenerator; -import com.destroystokyo.paper.event.executor.asm.ClassDefiner; -import com.google.common.base.Preconditions; -// Paper end - /** * Interface which defines the class for event call backs to plugins */ @@ -26,69 +17,25 @@ public interface EventExecutor { public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException; // Paper start - ConcurrentMap> eventExecutorMap = new ConcurrentHashMap>() { - @NotNull - @Override - public Class computeIfAbsent(@NotNull Method key, @NotNull Function> mappingFunction) { - Class executorClass = get(key); - if (executorClass != null) - return executorClass; - - //noinspection SynchronizationOnLocalVariableOrMethodParameter - synchronized (key) { - executorClass = get(key); - if (executorClass != null) - return executorClass; - - return super.computeIfAbsent(key, mappingFunction); - } - } - }; - @NotNull - public static EventExecutor create(@NotNull Method m, @NotNull Class eventClass) { + static EventExecutor create(@NotNull Method m, @NotNull Class eventClass) { Preconditions.checkNotNull(m, "Null method"); Preconditions.checkArgument(m.getParameterCount() != 0, "Incorrect number of arguments %s", m.getParameterCount()); Preconditions.checkArgument(m.getParameterTypes()[0] == eventClass, "First parameter %s doesn't match event class %s", m.getParameterTypes()[0], eventClass); - ClassDefiner definer = ClassDefiner.getInstance(); if (m.getReturnType() != Void.TYPE) { - final org.bukkit.plugin.java.JavaPlugin plugin = org.bukkit.plugin.java.JavaPlugin.getProvidingPlugin(m.getDeclaringClass()); + final JavaPlugin plugin = JavaPlugin.getProvidingPlugin(m.getDeclaringClass()); org.bukkit.Bukkit.getLogger().warning("@EventHandler method " + m.getDeclaringClass().getName() + (Modifier.isStatic(m.getModifiers()) ? '.' : '#') + m.getName() + " returns non-void type " + m.getReturnType().getName() + ". This is unsupported behavior and will no longer work in a future version of Paper." + " This should be reported to the developers of " + plugin.getPluginMeta().getDisplayName() + " (" + String.join(",", plugin.getPluginMeta().getAuthors()) + ')'); } - if (Modifier.isStatic(m.getModifiers())) { - return new StaticMethodHandleEventExecutor(eventClass, m); - } else if (definer.isBypassAccessChecks() || Modifier.isPublic(m.getDeclaringClass().getModifiers()) && Modifier.isPublic(m.getModifiers())) { - // get the existing generated EventExecutor class for the Method or generate one - Class executorClass = eventExecutorMap.computeIfAbsent(m, (__) -> { - String name = ASMEventExecutorGenerator.generateName(); - byte[] classData = ASMEventExecutorGenerator.generateEventExecutor(m, name); - return definer.defineClass(m.getDeclaringClass().getClassLoader(), name, classData).asSubclass(EventExecutor.class); - }); - - try { - EventExecutor asmExecutor = executorClass.newInstance(); - // Define a wrapper to conform to bukkit stupidity (passing in events that don't match and wrapper exception) - return new EventExecutor() { - @Override - public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException { - if (!eventClass.isInstance(event)) return; - asmExecutor.execute(listener, event); - } - - @Override - @NotNull - public String toString() { - return "ASMEventExecutor['" + m + "']"; - } - }; - } catch (InstantiationException | IllegalAccessException e) { - throw new AssertionError("Unable to initialize generated event executor", e); - } - } else { - return new MethodHandleEventExecutor(eventClass, m); + if (!m.trySetAccessible()) { + final JavaPlugin plugin = JavaPlugin.getProvidingPlugin(m.getDeclaringClass()); + throw new AssertionError( + "@EventHandler method " + m.getDeclaringClass().getName() + (Modifier.isStatic(m.getModifiers()) ? '.' : '#') + m.getName() + " is not accessible." + + " This should be reported to the developers of " + plugin.getDescription().getName() + " (" + String.join(",", plugin.getDescription().getAuthors()) + ')' + ); } + return EventExecutorFactory.create(m, eventClass); } // Paper end } diff --git a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java index e0203f199..60a8a2cca 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java +++ b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java @@ -319,7 +319,7 @@ public abstract class JavaPlugin extends PluginBase { * {@inheritDoc} */ @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) { return false; } @@ -328,7 +328,7 @@ public abstract class JavaPlugin extends PluginBase { */ @Override @Nullable - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String @NotNull [] args) { return null; } diff --git a/paper-api/src/main/java/org/bukkit/plugin/messaging/Messenger.java b/paper-api/src/main/java/org/bukkit/plugin/messaging/Messenger.java index 754fac6b2..2554d383d 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/messaging/Messenger.java +++ b/paper-api/src/main/java/org/bukkit/plugin/messaging/Messenger.java @@ -229,5 +229,5 @@ public interface Messenger { * @param channel Channel that the message was sent by. * @param message Raw payload of the message. */ - public void dispatchIncomingMessage(@NotNull Player source, @NotNull String channel, @NotNull byte[] message); + public void dispatchIncomingMessage(@NotNull Player source, @NotNull String channel, byte @NotNull [] message); } diff --git a/paper-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java b/paper-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java index eb962efd5..70463cd05 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java +++ b/paper-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java @@ -17,5 +17,5 @@ public interface PluginMessageListener { * @param player Source of the message. * @param message The raw message that was sent. */ - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message); + public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message); } diff --git a/paper-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java b/paper-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java index 281ae60a6..4a350155b 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java +++ b/paper-api/src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java @@ -27,7 +27,7 @@ public interface PluginMessageRecipient { * @throws ChannelNotRegisteredException Thrown if the channel is not * registered for this plugin. */ - public void sendPluginMessage(@NotNull Plugin source, @NotNull String channel, @NotNull byte[] message); + public void sendPluginMessage(@NotNull Plugin source, @NotNull String channel, byte @NotNull [] message); /** * Gets a set containing all the Plugin Channels that this client is diff --git a/paper-api/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java b/paper-api/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java index 7449d2f78..5580fdfe8 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java +++ b/paper-api/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java @@ -439,7 +439,7 @@ public class StandardMessenger implements Messenger { } @Override - public void dispatchIncomingMessage(@NotNull Player source, @NotNull String channel, @NotNull byte[] message) { + public void dispatchIncomingMessage(@NotNull Player source, @NotNull String channel, byte @NotNull [] message) { if (source == null) { throw new IllegalArgumentException("Player source cannot be null"); } @@ -534,7 +534,7 @@ public class StandardMessenger implements Messenger { * @throws ChannelNotRegisteredException Thrown if the channel is not * registered for this plugin. */ - public static void validatePluginMessage(@NotNull Messenger messenger, @NotNull Plugin source, @NotNull String channel, @NotNull byte[] message) { + public static void validatePluginMessage(@NotNull Messenger messenger, @NotNull Plugin source, @NotNull String channel, byte @NotNull [] message) { if (messenger == null) { throw new IllegalArgumentException("Messenger cannot be null"); } diff --git a/paper-api/src/main/java/org/bukkit/util/ChatPaginator.java b/paper-api/src/main/java/org/bukkit/util/ChatPaginator.java index dea3b53c2..1f4abed00 100644 --- a/paper-api/src/main/java/org/bukkit/util/ChatPaginator.java +++ b/paper-api/src/main/java/org/bukkit/util/ChatPaginator.java @@ -150,7 +150,7 @@ public class ChatPaginator { private int pageNumber; private int totalPages; - public ChatPage(@NotNull String[] lines, int pageNumber, int totalPages) { + public ChatPage(@NotNull String @NotNull [] lines, int pageNumber, int totalPages) { this.lines = lines; this.pageNumber = pageNumber; this.totalPages = totalPages; @@ -165,7 +165,7 @@ public class ChatPaginator { } @NotNull - public String[] getLines() { + public String @NotNull [] getLines() { return lines; } } diff --git a/paper-api/src/main/java/org/bukkit/util/noise/OctaveGenerator.java b/paper-api/src/main/java/org/bukkit/util/noise/OctaveGenerator.java index 618ed706e..656014355 100644 --- a/paper-api/src/main/java/org/bukkit/util/noise/OctaveGenerator.java +++ b/paper-api/src/main/java/org/bukkit/util/noise/OctaveGenerator.java @@ -12,7 +12,7 @@ public abstract class OctaveGenerator { protected double yScale = 1; protected double zScale = 1; - protected OctaveGenerator(@NotNull NoiseGenerator[] octaves) { + protected OctaveGenerator(@NotNull NoiseGenerator @NotNull [] octaves) { this.octaves = octaves; } @@ -90,7 +90,7 @@ public abstract class OctaveGenerator { * @return Clone of the individual octaves */ @NotNull - public NoiseGenerator[] getOctaves() { + public NoiseGenerator @NotNull [] getOctaves() { return octaves.clone(); } diff --git a/paper-api/src/main/java/org/bukkit/util/noise/SimplexNoiseGenerator.java b/paper-api/src/main/java/org/bukkit/util/noise/SimplexNoiseGenerator.java index 775d8f403..30974a0e5 100644 --- a/paper-api/src/main/java/org/bukkit/util/noise/SimplexNoiseGenerator.java +++ b/paper-api/src/main/java/org/bukkit/util/noise/SimplexNoiseGenerator.java @@ -78,15 +78,15 @@ public class SimplexNoiseGenerator extends PerlinNoiseGenerator { offsetW = rand.nextDouble() * 256; } - protected static double dot(@NotNull int[] g, double x, double y) { + protected static double dot(int @NotNull [] g, double x, double y) { return g[0] * x + g[1] * y; } - protected static double dot(@NotNull int[] g, double x, double y, double z) { + protected static double dot(int @NotNull [] g, double x, double y, double z) { return g[0] * x + g[1] * y + g[2] * z; } - protected static double dot(@NotNull int[] g, double x, double y, double z, double w) { + protected static double dot(int @NotNull [] g, double x, double y, double z, double w) { return g[0] * x + g[1] * y + g[2] * z + g[3] * w; } diff --git a/paper-api/src/test/java/org/bukkit/AnnotationTest.java b/paper-api/src/test/java/org/bukkit/AnnotationTest.java index 5b0d26c68..37feafd62 100644 --- a/paper-api/src/test/java/org/bukkit/AnnotationTest.java +++ b/paper-api/src/test/java/org/bukkit/AnnotationTest.java @@ -205,14 +205,16 @@ public class AnnotationTest { // Paper start - skip class if it's @NullMarked private static boolean isClassNullMarked(@NotNull ClassNode clazz, @NotNull Map allClasses) { + if (isClassNullMarked0(clazz)) { + return true; + } if (clazz.nestHostClass != null) { final ClassNode nestHostNode = allClasses.get(clazz.nestHostClass); if (nestHostNode != null) { - return isClassNullMarked0(nestHostNode); + return isClassNullMarked(nestHostNode, allClasses); } } - - return isClassNullMarked0(clazz); + return false; } private static boolean isClassNullMarked0(@NotNull ClassNode clazz) { diff --git a/paper-server/build.gradle.kts b/paper-server/build.gradle.kts index baa638e0d..21385e4dd 100644 --- a/paper-server/build.gradle.kts +++ b/paper-server/build.gradle.kts @@ -11,7 +11,7 @@ plugins { val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" dependencies { - mache("io.papermc:mache:1.21.4+build.6") + mache("io.papermc:mache:1.21.4+build.7") paperclip("io.papermc:paperclip:3.0.3") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } @@ -128,7 +128,7 @@ dependencies { implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21 implementation("net.minecrell:terminalconsoleappender:1.3.0") - implementation("net.kyori:adventure-text-serializer-ansi:4.17.0") // Keep in sync with adventureVersion from Paper-API build file + implementation("net.kyori:adventure-text-serializer-ansi:4.18.0") // Keep in sync with adventureVersion from Paper-API build file /* Required to add the missing Log4j2Plugins.dat file from log4j-core diff --git a/paper-server/patches/features/0004-Anti-Xray.patch b/paper-server/patches/features/0004-Anti-Xray.patch index 92c417ecb..2a2def278 100644 --- a/paper-server/patches/features/0004-Anti-Xray.patch +++ b/paper-server/patches/features/0004-Anti-Xray.patch @@ -239,7 +239,7 @@ index 0f346faa82b988e86834c38837f6f11bea7f31c6..771d6ed6a7c889c09efd4ff6e20298c8 if (blockState == null) { // CraftBukkit start - remove blockstate if failed (or the same) diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java -index 809b3c37d3749c76c3c243cd91c593d03693e9b3..860d1c9729c4ee97ec6f40f7aa969829070b27c0 100644 +index c82780db7fe5b1557a7802d3111f38099be55ac1..d63d745a0220963a297cfedf1e8983aeb471a045 100644 --- a/net/minecraft/world/level/chunk/ChunkAccess.java +++ b/net/minecraft/world/level/chunk/ChunkAccess.java @@ -114,14 +114,14 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh @@ -261,7 +261,7 @@ index 809b3c37d3749c76c3c243cd91c593d03693e9b3..860d1c9729c4ee97ec6f40f7aa969829 } } diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 51a136cf015de730ca0d1b48cf618a2ed69ea89f..96b0342ab7b922aa16d07b6c00542e6cb66c974a 100644 +index ce781ba2c8b3f9f051201d3809a9cb041036f93a..cad71ac79fc52225a192aa5c5d07b13c831fc2c1 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -109,7 +109,7 @@ public class LevelChunk extends ChunkAccess { @@ -270,11 +270,11 @@ index 51a136cf015de730ca0d1b48cf618a2ed69ea89f..96b0342ab7b922aa16d07b6c00542e6c ) { - super(pos, data, level, level.registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); + super(pos, data, level, net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry - this.level = (net.minecraft.server.level.ServerLevel) level; // CraftBukkit - type + this.level = (ServerLevel) level; // CraftBukkit - type this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>(); diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java -index baa9f3e2e6e45c250930658e82bad70a3a292b05..fc21c3268c4b4fda2933d71f0913db28e3796653 100644 +index b3d600b0d6deaf44f232dfc86e1456c867e1c07f..a7fabde0f32f09d7f7bed65576ce469f069a21fa 100644 --- a/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/net/minecraft/world/level/chunk/LevelChunkSection.java @@ -38,9 +38,15 @@ public class LevelChunkSection { @@ -550,7 +550,7 @@ index bfbb1a2bb4abbb369a24f2f01439e9ea3e16794b..8d6ed8be4d93f7d4e6ea80c351020d88 int getSerializedSize(); diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -index 37437a86d74291fab1de9495008aafb15dfadce0..cf6e2053d81f7b0f8c8e58b9c0fad3285ebc047d 100644 +index c7c87bc8df86ceeef3e15a8f23fc252d4cee1984..3b55e7fb27d680204b8621666ae9200870def3eb 100644 --- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java @@ -94,7 +94,7 @@ public record SerializableChunkData( @@ -607,9 +607,9 @@ index 37437a86d74291fab1de9495008aafb15dfadce0..cf6e2053d81f7b0f8c8e58b9c0fad328 @@ -414,7 +418,7 @@ public record SerializableChunkData( // CraftBukkit start - read/write - private static Codec>> makeBiomeCodecRW(Registry iregistry) { -- return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS)); -+ return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS), null); // Paper - Anti-Xray - Add preset biomes + private static Codec>> makeBiomeCodecRW(Registry biomeRegistry) { +- return PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS)); ++ return PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS), null); // Paper - Anti-Xray - Add preset biomes } // CraftBukkit end diff --git a/paper-server/patches/features/0016-Rewrite-dataconverter-system.patch b/paper-server/patches/features/0016-Rewrite-dataconverter-system.patch index 1f0f67af8..ef5f7da45 100644 --- a/paper-server/patches/features/0016-Rewrite-dataconverter-system.patch +++ b/paper-server/patches/features/0016-Rewrite-dataconverter-system.patch @@ -30633,10 +30633,10 @@ index 0d65bf24f515b80701150fdc430f324a533cb478..b92a3da5c325e69f5601416d4205fb33 Thread thread = new ca.spottedleaf.moonrise.common.util.TickThread(() -> atomicReference.get().runServer(), "Server thread"); thread.setUncaughtExceptionHandler((thread1, exception) -> LOGGER.error("Uncaught exception in server thread", exception)); diff --git a/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/net/minecraft/world/level/chunk/storage/ChunkStorage.java -index 1b931e68634e72c3465a99aa29aa53009163046b..80bc7ad9ad076968d06279dedd845d5946cf2501 100644 +index 1a0455936cbca2d3812866b332e00a3aaacae8bd..5ba7f724687401c9ea1756a6491498e03548a257 100644 --- a/net/minecraft/world/level/chunk/storage/ChunkStorage.java +++ b/net/minecraft/world/level/chunk/storage/ChunkStorage.java -@@ -85,7 +85,7 @@ public class ChunkStorage implements AutoCloseable { +@@ -54,7 +54,7 @@ public class ChunkStorage implements AutoCloseable { } else { try { // CraftBukkit start @@ -30644,8 +30644,8 @@ index 1b931e68634e72c3465a99aa29aa53009163046b..80bc7ad9ad076968d06279dedd845d59 + if (false && version < 1466) { // Paper - no longer needed, data converter system / DFU handles it now CompoundTag level = chunkData.getCompound("Level"); if (level.getBoolean("TerrainPopulated") && !level.getBoolean("LightPopulated")) { - net.minecraft.server.level.ServerChunkCache cps = (generatoraccess == null) ? null : ((net.minecraft.server.level.ServerLevel) generatoraccess).getChunkSource(); -@@ -96,7 +96,7 @@ public class ChunkStorage implements AutoCloseable { + // Light is purged updating to 1.14+. We need to set light populated to true so the converter recognizes the chunk as being "full" +@@ -63,7 +63,7 @@ public class ChunkStorage implements AutoCloseable { } // CraftBukkit end if (version < 1493) { @@ -30654,7 +30654,7 @@ index 1b931e68634e72c3465a99aa29aa53009163046b..80bc7ad9ad076968d06279dedd845d59 if (chunkData.getCompound("Level").getBoolean("hasLegacyStructureData")) { LegacyStructureDataHandler legacyStructureHandler = this.getLegacyStructureHandler(levelKey, storage); chunkData = legacyStructureHandler.updateFromLegacy(chunkData); -@@ -113,7 +113,7 @@ public class ChunkStorage implements AutoCloseable { +@@ -80,7 +80,7 @@ public class ChunkStorage implements AutoCloseable { // Spigot end injectDatafixingContext(chunkData, levelKey, chunkGeneratorKey); @@ -30741,7 +30741,7 @@ index feb4cd21e26598509dc140028496a6f4254c0680..de43e54698125ce9f319d4889dd49f70 } diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java -index 6c9640f5432e9110e7811b6db246d268c6243feb..45f2800c4862a726490048576fca8e1f24252676 100644 +index d68c6c978ac0f154ffe541fd204bec03f033ae96..c44110b123ba5912af18faf0065e9ded780da9b7 100644 --- a/net/minecraft/world/level/storage/PlayerDataStorage.java +++ b/net/minecraft/world/level/storage/PlayerDataStorage.java @@ -115,7 +115,7 @@ public class PlayerDataStorage { diff --git a/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch index 337302aaa..13e59780d 100644 --- a/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch +++ b/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch @@ -23508,10 +23508,10 @@ index 3d3eec1db91cb47395f40c4f47aa77164ad42175..216f97207dac88cc1dc3df59c6ee8a62 + // Paper end - optimise collisions } diff --git a/net/minecraft/core/MappedRegistry.java b/net/minecraft/core/MappedRegistry.java -index 47b1fafd91b39e73c4e9134b0b8048000fba108a..76994c1491221c06cca5405ba239e6ff642b19ed 100644 +index 452c358c2cfa0c39e0b09853cd4a9a12c6ced65d..5f752603aa5611ce9d3dd44cc5b70c27ac46a86e 100644 --- a/net/minecraft/core/MappedRegistry.java +++ b/net/minecraft/core/MappedRegistry.java -@@ -50,6 +50,19 @@ public class MappedRegistry implements WritableRegistry { +@@ -51,6 +51,19 @@ public class MappedRegistry implements WritableRegistry { return this.getTags(); } @@ -23531,10 +23531,10 @@ index 47b1fafd91b39e73c4e9134b0b8048000fba108a..76994c1491221c06cca5405ba239e6ff public MappedRegistry(ResourceKey> key, Lifecycle registryLifecycle) { this(key, registryLifecycle, false); } -@@ -114,6 +127,7 @@ public class MappedRegistry implements WritableRegistry { - this.toId.put(value, size); +@@ -116,6 +129,7 @@ public class MappedRegistry implements WritableRegistry { this.registrationInfos.put(key, registrationInfo); this.registryLifecycle = this.registryLifecycle.add(registrationInfo.lifecycle()); + this.temporaryUnfrozenMap.put(key.location(), value); // Paper - support pre-filling in registry mod API + this.injectFluidRegister(key, value); // Paper - fluid method optimisations return reference; } @@ -26719,7 +26719,7 @@ index 2f49dbc919f7f5eea9abce6106723c72f5ae45fb..87d4291a3944f706a694536da6de0f28 } } diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index 70f6d068b3f3665b282d9750310c883839120ab2..870b9efd445ddadb3725e88351555ad986ce7c72 100644 +index da793ad12565c36fffb26eb771ff68c76632caf7..db06f966077928419bfe469260f04d7dfda69f28 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java @@ -91,6 +91,11 @@ public class ServerEntity { @@ -27496,7 +27496,7 @@ index 192977dd661ee795ada13db895db770293e9b402..95a4e37a3c93f9b3c56c7a7376ed521c } diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index ff5889f8fed0707a6654d9d21862e32e2ebc866d..e61fe83479f095e8addbd3e8f1d5179c998ae1eb 100644 +index 097ec55166b9e9269142be58992c29687122fe28..aeabb79512aabd7a9e8af1be72e1745f0e7eefe4 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -178,7 +178,7 @@ import net.minecraft.world.scores.Team; @@ -28372,7 +28372,7 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896 + // Paper end - block counting } diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab6814aef9ee 100644 +index 189385600b9094291152035b17df869eaccc0428..25a1089a7376f0cbd96bb43b5c203640c88fc282 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -135,7 +135,7 @@ import net.minecraft.world.scores.ScoreHolder; @@ -28725,7 +28725,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68 } private static float[] collectCandidateStepUpHeights(AABB box, List colliders, float deltaY, float maxUpStep) { -@@ -2664,23 +2812,110 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2662,23 +2810,110 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public boolean isInWall() { @@ -28849,7 +28849,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68 } public InteractionResult interact(Player player, InteractionHand hand) { -@@ -4104,15 +4339,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4102,15 +4337,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public Iterable getIndirectPassengers() { @@ -28875,7 +28875,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68 } public int countPlayerPassengers() { -@@ -4250,77 +4487,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4248,77 +4485,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return Mth.lerp(partialTick, this.yRotO, this.yRot); } @@ -29066,7 +29066,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68 public boolean touchingUnloadedChunk() { AABB aabb = this.getBoundingBox().inflate(1.0); -@@ -4473,6 +4769,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4471,6 +4767,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.setPosRaw(x, y, z, false); } public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) { @@ -29082,7 +29082,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68 if (!checkPosition(this, x, y, z)) { return; } -@@ -4603,6 +4908,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4601,6 +4906,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @Override public final void setRemoved(Entity.RemovalReason removalReason, org.bukkit.event.entity.EntityRemoveEvent.Cause cause) { @@ -29095,7 +29095,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68 org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit end final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers -@@ -4614,7 +4925,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4612,7 +4923,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.stopRiding(); } @@ -29104,7 +29104,7 @@ index 45f69a914d5a0565196c4105d61541047301470f..f42bdae2f80805e5069212bd16e3ab68 this.levelCallback.onRemove(removalReason); this.onRemoval(removalReason); // Paper start - Folia schedulers -@@ -4648,7 +4959,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4646,7 +4957,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess public boolean shouldBeSaved() { return (this.removalReason == null || this.removalReason.shouldSave()) && !this.isPassenger() @@ -30907,7 +30907,7 @@ index 8d98cba3830dc5dfb5cae9a6f5fedfffee0d2cd8..73962e79a0f3d892e3155443a1b84508 public interface NoiseBiomeSource { diff --git a/net/minecraft/world/level/block/Block.java b/net/minecraft/world/level/block/Block.java -index 91d7d250f7c3de8a71aef26e23c12764b06b322b..0d36b1ac7d54283af71f2494accded11c059dba5 100644 +index a4eb9a10b6c1351c7341bc031bb9f819e17ff1e5..976de81d65b6494cdad20f4ec5125fceec86f951 100644 --- a/net/minecraft/world/level/block/Block.java +++ b/net/minecraft/world/level/block/Block.java @@ -259,7 +259,7 @@ public class Block extends BlockBehaviour implements ItemLike { @@ -30920,7 +30920,7 @@ index 91d7d250f7c3de8a71aef26e23c12764b06b322b..0d36b1ac7d54283af71f2494accded11 public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java -index 25e49a24cedfa8ad04245d59fcac3231bcd62103..061d94a35d957ca72a01bae959d38aab59b1a64d 100644 +index fa816de449a6077c05fee76835a1625f5b2008ac..5473b4006f7e0266ea11a7b05cef78a113c30d97 100644 --- a/net/minecraft/world/level/block/state/BlockBehaviour.java +++ b/net/minecraft/world/level/block/state/BlockBehaviour.java @@ -416,7 +416,7 @@ public abstract class BlockBehaviour implements FeatureElement { @@ -31399,7 +31399,7 @@ index 92350434746f06bbf4a161c6bc42602de7b45220..1c24f38d21da1be9740512981f219924 public Property.Value value(T value) { diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java -index 860d1c9729c4ee97ec6f40f7aa969829070b27c0..94de1217d18e1a7a0fb7b83f21436eaf0a5998c6 100644 +index d63d745a0220963a297cfedf1e8983aeb471a045..6d565b52552534ce9cacfc35ad1bf4adcb69eac3 100644 --- a/net/minecraft/world/level/chunk/ChunkAccess.java +++ b/net/minecraft/world/level/chunk/ChunkAccess.java @@ -57,7 +57,7 @@ import net.minecraft.world.ticks.SavedTick; @@ -31537,8 +31537,8 @@ index 860d1c9729c4ee97ec6f40f7aa969829070b27c0..94de1217d18e1a7a0fb7b83f21436eaf + // Paper end - get block chunk optimisation } // CraftBukkit start - public void setBiome(int i, int j, int k, Holder biome) { -@@ -507,12 +572,12 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + public void setBiome(int x, int y, int z, Holder biome) { +@@ -503,12 +568,12 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh } public void initializeLightSources() { @@ -31554,7 +31554,7 @@ index 860d1c9729c4ee97ec6f40f7aa969829070b27c0..94de1217d18e1a7a0fb7b83f21436eaf public record PackedTicks(List> blocks, List> fluids) { diff --git a/net/minecraft/world/level/chunk/ChunkGenerator.java b/net/minecraft/world/level/chunk/ChunkGenerator.java -index 65117a9cb9d1b8684cae8d36ea3b8e2050fb928c..a9d65e28b849c9660a14ef7c16ed17bd5182bd7e 100644 +index b320519578f7e2c52b2116afb247e071b322696e..6ed51cf42b5864194d671b5b56f5b9bdf0291dc0 100644 --- a/net/minecraft/world/level/chunk/ChunkGenerator.java +++ b/net/minecraft/world/level/chunk/ChunkGenerator.java @@ -116,7 +116,7 @@ public abstract class ChunkGenerator { @@ -31717,7 +31717,7 @@ index e7c0f4da8508fbca467326f475668d66454d7b77..41856c98d97e7eb0782f8e441b9a269a @Override public BlockEntity getBlockEntity(BlockPos pos) { diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 96b0342ab7b922aa16d07b6c00542e6cb66c974a..c1ae7755e8d6fa8501d2210dab7605d993c55722 100644 +index cad71ac79fc52225a192aa5c5d07b13c831fc2c1..761fdcd4a4e18f45547afd8edff44f61c6eeacb4 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -52,7 +52,7 @@ import net.minecraft.world.ticks.LevelChunkTicks; @@ -31896,7 +31896,7 @@ index 96b0342ab7b922aa16d07b6c00542e6cb66c974a..c1ae7755e8d6fa8501d2210dab7605d9 @Nullable diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java -index fc21c3268c4b4fda2933d71f0913db28e3796653..2ff691bcd32f587e8add2d8eda7e7339ccbde6e8 100644 +index a7fabde0f32f09d7f7bed65576ce469f069a21fa..412e7b1cf8c24f0ddf6d174967bedad576f10aba 100644 --- a/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/net/minecraft/world/level/chunk/LevelChunkSection.java @@ -13,7 +13,7 @@ import net.minecraft.world.level.block.Blocks; @@ -32627,7 +32627,7 @@ index 7a4d299d2ce36982204e30de9278ddfd5b37c3df..b8348976e80578d9eff64eea68c04c60 private final ChunkStatus status; @Nullable diff --git a/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/net/minecraft/world/level/chunk/storage/ChunkStorage.java -index 80bc7ad9ad076968d06279dedd845d5946cf2501..433feab7f7c1931f79836164a0b8c4a1c3b75ba6 100644 +index 5ba7f724687401c9ea1756a6491498e03548a257..64343a3f6e6009eed5b392482ec053be8e00ee63 100644 --- a/net/minecraft/world/level/chunk/storage/ChunkStorage.java +++ b/net/minecraft/world/level/chunk/storage/ChunkStorage.java @@ -22,20 +22,30 @@ import net.minecraft.world.level.chunk.ChunkGenerator; @@ -32665,7 +32665,7 @@ index 80bc7ad9ad076968d06279dedd845d5946cf2501..433feab7f7c1931f79836164a0b8c4a1 } // CraftBukkit start -@@ -99,7 +109,9 @@ public class ChunkStorage implements AutoCloseable { +@@ -66,7 +76,9 @@ public class ChunkStorage implements AutoCloseable { chunkData = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, chunkData, version, 1493); // Paper - replace chunk converter if (chunkData.getCompound("Level").getBoolean("hasLegacyStructureData")) { LegacyStructureDataHandler legacyStructureHandler = this.getLegacyStructureHandler(levelKey, storage); @@ -32675,7 +32675,7 @@ index 80bc7ad9ad076968d06279dedd845d5946cf2501..433feab7f7c1931f79836164a0b8c4a1 } } -@@ -163,7 +175,13 @@ public class ChunkStorage implements AutoCloseable { +@@ -130,7 +142,13 @@ public class ChunkStorage implements AutoCloseable { } public CompletableFuture> read(ChunkPos chunkPos) { @@ -32690,7 +32690,7 @@ index 80bc7ad9ad076968d06279dedd845d5946cf2501..433feab7f7c1931f79836164a0b8c4a1 } public CompletableFuture write(ChunkPos pos, Supplier tagSupplier) { -@@ -179,29 +197,54 @@ public class ChunkStorage implements AutoCloseable { +@@ -146,29 +164,54 @@ public class ChunkStorage implements AutoCloseable { }; // Paper end - guard against possible chunk pos desync this.handleLegacyStructureIndex(pos); @@ -32784,7 +32784,7 @@ index 889e188e920edb284f04b264bcdd06146f54a4cb..2199a9e2a0141c646d108f2687a27f1d private final Long2ObjectLinkedOpenHashMap> regionCacheForBlender = new Long2ObjectLinkedOpenHashMap<>(); private static final int REGION_CACHE_SIZE = 1024; diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java -index 783a2d80f6197dd0af0dc81909f0353a8ea2ecf4..7da388ffab162c282cad0f297bb7304f3c2abbaf 100644 +index 4eb07097986aac67421dd8e6a17cc5436da91187..984db72272d552c7210bd6f437ea88694ddd2828 100644 --- a/net/minecraft/world/level/chunk/storage/RegionFile.java +++ b/net/minecraft/world/level/chunk/storage/RegionFile.java @@ -22,7 +22,7 @@ import net.minecraft.util.profiling.jfr.JvmProfiler; @@ -33324,7 +33324,7 @@ index 7dc1ffffd9d0fec54dbc254c154ee85ee750174d..778bd73a938c94ecb85ca0f8b686ff4e record PackedChunk(Int2ObjectMap sectionsByY, boolean versionChanged) { diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -index cf6e2053d81f7b0f8c8e58b9c0fad3285ebc047d..70a9972252576e039ac126f6057a6ed66b80cdfc 100644 +index 3b55e7fb27d680204b8621666ae9200870def3eb..879d411775a2fece1d8a970300cb3a550baa6305 100644 --- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java @@ -148,7 +148,7 @@ public record SerializableChunkData( @@ -33700,10 +33700,10 @@ index 342c83309b19c64d86e0dd97c1756c96be52772b..423779a2b690f387a4f0bd07b97b50e0 } } diff --git a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -index 29d9f6e54421c539e9e55ab9f51b4c872da3fbb8..d77016287f5f9a0964d56f05d2d5256ef2e6e86c 100644 +index 6d61739574155f89511b9adcaf1174841bdc7da7..65728ef17e63d71833677fdcbd5bb90794b4822b 100644 --- a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java +++ b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -@@ -78,7 +78,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { +@@ -77,7 +77,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { return CompletableFuture.supplyAsync(() -> { this.doCreateBiomes(blender, randomState, structureManager, chunk); return chunk; @@ -33712,7 +33712,7 @@ index 29d9f6e54421c539e9e55ab9f51b4c872da3fbb8..d77016287f5f9a0964d56f05d2d5256e } private void doCreateBiomes(Blender blender, RandomState random, StructureManager structureManager, ChunkAccess chunk) { -@@ -318,7 +318,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { +@@ -317,7 +317,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { } return var20; @@ -34002,7 +34002,7 @@ index ca23af013967b50420ebee178878ea79333de53b..d41b9266625ca6c5e32c5126f35a1f77 public int getLightSectionCount() { diff --git a/net/minecraft/world/level/material/FlowingFluid.java b/net/minecraft/world/level/material/FlowingFluid.java -index 130ef38a50f1df1faa26b433b0c605a4507f71af..f6daca279788c3d983a9ee213df85d5d93fc6eed 100644 +index 86fd2b831f42bdb5bd045e44b26489d5a8697dd9..e30499bdcd6600e5c9d4a755c1182fb6dff3735f 100644 --- a/net/minecraft/world/level/material/FlowingFluid.java +++ b/net/minecraft/world/level/material/FlowingFluid.java @@ -45,6 +45,48 @@ public abstract class FlowingFluid extends Fluid { diff --git a/paper-server/patches/features/0018-Fix-entity-tracker-desync-when-new-players-are-added.patch b/paper-server/patches/features/0018-Fix-entity-tracker-desync-when-new-players-are-added.patch index 1059713e9..4bfb85602 100644 --- a/paper-server/patches/features/0018-Fix-entity-tracker-desync-when-new-players-are-added.patch +++ b/paper-server/patches/features/0018-Fix-entity-tracker-desync-when-new-players-are-added.patch @@ -60,7 +60,7 @@ index 3dff97f13586be3b52bbe786852c185f6753a019..ff6503bf8eb88d1264c3d848a89d0255 } else if (this.seenBy.remove(player.connection)) { this.serverEntity.removePairing(player); diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java -index 870b9efd445ddadb3725e88351555ad986ce7c72..a4da36060ca75968f5831adfc3f7117281649b7a 100644 +index db06f966077928419bfe469260f04d7dfda69f28..0fb253aa55a24b56b17f524b3261c5b75c7d7e59 100644 --- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java @@ -90,6 +90,13 @@ public class ServerEntity { @@ -77,7 +77,7 @@ index 870b9efd445ddadb3725e88351555ad986ce7c72..a4da36060ca75968f5831adfc3f71172 public void sendChanges() { // Paper start - optimise collisions if (((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this.entity).moonrise$isHardColliding()) { -@@ -130,7 +137,7 @@ public class ServerEntity { +@@ -131,7 +138,7 @@ public class ServerEntity { this.sendDirtyEntityData(); } @@ -86,7 +86,7 @@ index 870b9efd445ddadb3725e88351555ad986ce7c72..a4da36060ca75968f5831adfc3f71172 byte b = Mth.packDegrees(this.entity.getYRot()); byte b1 = Mth.packDegrees(this.entity.getXRot()); boolean flag = Math.abs(b - this.lastSentYRot) >= 1 || Math.abs(b1 - this.lastSentXRot) >= 1; -@@ -165,7 +172,7 @@ public class ServerEntity { +@@ -166,7 +173,7 @@ public class ServerEntity { long l1 = this.positionCodec.encodeY(vec3); long l2 = this.positionCodec.encodeZ(vec3); boolean flag5 = l < -32768L || l > 32767L || l1 < -32768L || l1 > 32767L || l2 < -32768L || l2 > 32767L; @@ -95,7 +95,7 @@ index 870b9efd445ddadb3725e88351555ad986ce7c72..a4da36060ca75968f5831adfc3f71172 this.wasOnGround = this.entity.onGround(); this.teleportDelay = 0; packet = ClientboundEntityPositionSyncPacket.of(this.entity); -@@ -230,6 +237,7 @@ public class ServerEntity { +@@ -231,6 +238,7 @@ public class ServerEntity { } this.entity.hasImpulse = false; diff --git a/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch b/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch index 400d714ab..2a92380a2 100644 --- a/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/MappedRegistry.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/core/MappedRegistry.java +++ b/net/minecraft/core/MappedRegistry.java -@@ -33,11 +_,11 @@ +@@ -33,17 +_,18 @@ public class MappedRegistry implements WritableRegistry { private final ResourceKey> key; private final ObjectList> byId = new ObjectArrayList<>(256); @@ -17,6 +17,29 @@ private Lifecycle registryLifecycle; private final Map, HolderSet.Named> frozenTags = new IdentityHashMap<>(); MappedRegistry.TagSet allTags = MappedRegistry.TagSet.unbound(); + private boolean frozen; + @Nullable + private Map> unregisteredIntrusiveHolders; ++ public final Map temporaryUnfrozenMap = new HashMap<>(); // Paper - support pre-filling in registry mod API + + @Override + public Stream> listTags() { +@@ -114,6 +_,7 @@ + this.toId.put(value, size); + this.registrationInfos.put(key, registrationInfo); + this.registryLifecycle = this.registryLifecycle.add(registrationInfo.lifecycle()); ++ this.temporaryUnfrozenMap.put(key.location(), value); // Paper - support pre-filling in registry mod API + return reference; + } + } +@@ -275,6 +_,7 @@ + return this; + } else { + this.frozen = true; ++ this.temporaryUnfrozenMap.clear(); // Paper - support pre-filling in registry mod API + this.byValue.forEach((object, reference) -> reference.bindValue((T)object)); + List list = this.byKey + .entrySet() @@ -509,4 +_,13 @@ Stream> getTags(); diff --git a/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch b/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch index bc224c349..36c52e0a7 100644 --- a/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/registries/BuiltInRegistries.java.patch @@ -26,7 +26,7 @@ ResourceLocation resourceLocation = key.location(); LOADERS.put(resourceLocation, () -> bootstrap.run(registry)); WRITABLE_REGISTRY.register((ResourceKey)key, registry, RegistrationInfo.BUILT_IN); -@@ -328,7 +_,14 @@ +@@ -328,16 +_,34 @@ } public static void bootStrap() { @@ -41,6 +41,26 @@ freeze(); validate(REGISTRY); } + + private static void createContents() { ++ // Paper start - class-load org.bukkit.Registry ++ // we have to class-load Registry here to create all the CraftRegistry instances ++ // that will be created when Registry is class-loaded before RegistryAccess#getRegistry ++ // would try to create them in lockReferenceHolder ++ try { ++ Class.forName(org.bukkit.Registry.class.getName()); ++ } catch (final ClassNotFoundException ex) { ++ throw new RuntimeException(ex); ++ } ++ // Paper end - class-load org.bukkit.Registry + LOADERS.forEach((resourceLocation, supplier) -> { + if (supplier.get() == null) { + LOGGER.error("Unable to bootstrap registry '{}'", resourceLocation); + } ++ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(ResourceKey.createRegistryKey(resourceLocation)); // Paper - lock reference holder creation + }); + } + @@ -346,6 +_,7 @@ for (Registry registry : REGISTRY) { diff --git a/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch b/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch index c625d649d..97a14239e 100644 --- a/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch +++ b/paper-server/patches/sources/net/minecraft/resources/RegistryDataLoader.java.patch @@ -34,11 +34,12 @@ } catch (Exception var14) { loadingErrors.put( resourceKey, -@@ -283,7 +_,8 @@ +@@ -283,7 +_,9 @@ } } - TagLoader.loadTagsForRegistry(resourceManager, registry); ++ io.papermc.paper.registry.PaperRegistryAccess.instance().lockReferenceHolders(registry.key()); // Paper - lock reference holders + io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), conversions); // Paper - run pre-freeze listeners + TagLoader.loadTagsForRegistry(resourceManager, registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - tag lifecycle - add cause } diff --git a/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch b/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch index d8518cce1..a56079b3c 100644 --- a/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/MinecraftServer.java.patch @@ -1193,15 +1193,18 @@ return ReloadableServerResources.loadResources( closeableResourceManager, this.registries, -@@ -1520,6 +_,7 @@ +@@ -1520,20 +_,39 @@ ) .thenAcceptAsync( reloadableResources -> { + io.papermc.paper.command.brigadier.PaperBrigadier.moveBukkitCommands(this.resources.managers().getCommands(), reloadableResources.managers().commands); // Paper this.resources.close(); this.resources = reloadableResources; - this.packRepository.setSelected(selectedIds); -@@ -1529,11 +_,29 @@ +- this.packRepository.setSelected(selectedIds); ++ this.packRepository.setSelected(selectedIds, false); // Paper - add pendingReload flag to determine required pack loading - false as this is *after* a reload (see above) + WorldDataConfiguration worldDataConfiguration = new WorldDataConfiguration( + getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures() + ); this.worldData.setDataConfiguration(worldDataConfiguration); this.resources.managers.updateStaticRegistryTags(); this.resources.managers.getRecipeManager().finalizeRecipeLoading(this.worldData.enabledFeatures()); @@ -1232,6 +1235,33 @@ }, this ); +@@ -1550,7 +_,7 @@ + DataPackConfig dataPackConfig = initialDataConfig.dataPacks(); + FeatureFlagSet featureFlagSet = initMode ? FeatureFlagSet.of() : initialDataConfig.enabledFeatures(); + FeatureFlagSet featureFlagSet1 = initMode ? FeatureFlags.REGISTRY.allFlags() : initialDataConfig.enabledFeatures(); +- packRepository.reload(); ++ packRepository.reload(true); // Paper - will load resource packs + if (safeMode) { + return configureRepositoryWithSelection(packRepository, List.of("vanilla"), featureFlagSet, false); + } else { +@@ -1605,7 +_,7 @@ + private static WorldDataConfiguration configureRepositoryWithSelection( + PackRepository packRepository, Collection selectedPacks, FeatureFlagSet enabledFeatures, boolean safeMode + ) { +- packRepository.setSelected(selectedPacks); ++ packRepository.setSelected(selectedPacks, true); // Paper - add pendingReload flag to determine required pack loading - before the initial server load + enableForcedFeaturePacks(packRepository, enabledFeatures); + DataPackConfig selectedPacks1 = getSelectedPacks(packRepository, safeMode); + FeatureFlagSet featureFlagSet = packRepository.getRequestedFeatureFlags().join(enabledFeatures); +@@ -1637,7 +_,7 @@ + } + } + +- packRepository.setSelected(set); ++ packRepository.setSelected(set, true); // Paper - add pendingReload flag to determine required pack loading - before the initial server start + } + } + @@ -1652,10 +_,11 @@ if (this.isEnforceWhitelist()) { PlayerList playerList = commandSource.getServer().getPlayerList(); diff --git a/paper-server/patches/sources/net/minecraft/server/commands/ReloadCommand.java.patch b/paper-server/patches/sources/net/minecraft/server/commands/ReloadCommand.java.patch index 47fc313fa..cfcc1b2b5 100644 --- a/paper-server/patches/sources/net/minecraft/server/commands/ReloadCommand.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/commands/ReloadCommand.java.patch @@ -9,6 +9,15 @@ LOGGER.warn("Failed to execute reload", throwable); source.sendFailure(Component.translatable("commands.reload.failure")); return null; +@@ -24,7 +_,7 @@ + } + + private static Collection discoverNewPacks(PackRepository packRepository, WorldData worldData, Collection selectedIds) { +- packRepository.reload(); ++ packRepository.reload(true); // Paper - will perform a full reload + Collection list = Lists.newArrayList(selectedIds); + Collection disabled = worldData.getDataConfiguration().dataPacks().getDisabled(); + @@ -36,6 +_,16 @@ return list; diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerEntity.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerEntity.java.patch index 4413d91d0..7932d7570 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerEntity.java.patch @@ -29,7 +29,7 @@ removedPassengers(passengers, this.lastPassengers) .forEach( removedPassenger -> { -@@ -102,10 +_,10 @@ +@@ -102,13 +_,14 @@ this.lastPassengers = passengers; } @@ -42,7 +42,12 @@ + MapId mapId = itemFrame.cachedMapId; // Paper - Perf: Cache map ids on item frames MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level); if (savedData != null) { - for (ServerPlayer serverPlayer : this.level.players()) { +- for (ServerPlayer serverPlayer : this.level.players()) { ++ for (final net.minecraft.server.network.ServerPlayerConnection connection : this.trackedPlayers) { // Paper ++ final ServerPlayer serverPlayer = connection.getPlayer(); // Paper + savedData.tickCarriedBy(serverPlayer, item); + Packet updatePacket = savedData.getUpdatePacket(mapId, serverPlayer); + if (updatePacket != null) { @@ -141,7 +_,13 @@ } else { this.teleportDelay++; diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch index 662414567..bfb5d7cd0 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -1342,17 +1342,18 @@ } public SectionPos getLastSectionPos() { -@@ -1930,21 +_,54 @@ +@@ -1930,21 +_,55 @@ } @Override - public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean traceItem) { -+ public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean traceItem, boolean callEvent) { // CraftBukkit - SPIGOT-2942: Add boolean to call event ++ public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean traceItem, boolean callEvent, @Nullable java.util.function.Consumer entityOperation) { // Paper start - Extend HumanEntity#dropItem API ItemEntity itemEntity = this.createItemStackToDrop(droppedItem, dropAround, traceItem); if (itemEntity == null) { return null; } else { + // CraftBukkit start - fire PlayerDropItemEvent ++ if (entityOperation != null) entityOperation.accept((org.bukkit.entity.Item) itemEntity.getBukkitEntity()); + if (callEvent) { + org.bukkit.entity.Player player = this.getBukkitEntity(); + org.bukkit.entity.Item drop = (org.bukkit.entity.Item) itemEntity.getBukkitEntity(); diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch index e21dc4c54..af6cdd217 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch @@ -95,7 +95,7 @@ } } -@@ -88,30 +_,117 @@ +@@ -88,30 +_,119 @@ public void handlePong(ServerboundPongPacket packet) { } @@ -208,15 +208,16 @@ Profiler.get().push("keepAlive"); long millis = Util.getMillis(); - if (!this.isSingleplayerOwner() && millis - this.keepAliveTime >= 15000L) { -- if (this.keepAlivePending) { -- this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE); + // Paper start - give clients a longer time to respond to pings as per pre 1.12.2 timings + // This should effectively place the keepalive handling back to "as it was" before 1.12.2 + final long elapsedTime = millis - this.keepAliveTime; + if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // use vanilla's 15000L between keep alive packets -+ if (this.keepAlivePending && !this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected + if (this.keepAlivePending) { +- this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE); ++ if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected ++ this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause ++ } + // Paper end - give clients a longer time to respond to pings as per pre 1.12.2 timings -+ this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause } else if (this.checkIfClosed(millis)) { this.keepAlivePending = true; this.keepAliveTime = millis; diff --git a/paper-server/patches/sources/net/minecraft/server/packs/repository/PackRepository.java.patch b/paper-server/patches/sources/net/minecraft/server/packs/repository/PackRepository.java.patch new file mode 100644 index 000000000..8a7d513c1 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/server/packs/repository/PackRepository.java.patch @@ -0,0 +1,76 @@ +--- a/net/minecraft/server/packs/repository/PackRepository.java ++++ b/net/minecraft/server/packs/repository/PackRepository.java +@@ -21,9 +_,13 @@ + private final Set sources; + private Map available = ImmutableMap.of(); + private List selected = ImmutableList.of(); ++ private final net.minecraft.world.level.validation.DirectoryValidator validator; // Paper - add validator + +- public PackRepository(RepositorySource... sources) { +- this.sources = ImmutableSet.copyOf(sources); ++ // Paper start - add validator ++ public PackRepository(net.minecraft.world.level.validation.DirectoryValidator validator, RepositorySource... providers) { ++ this.validator = validator; ++ // Paper end - add validator ++ this.sources = ImmutableSet.copyOf(providers); + } + + public static String displayPackList(Collection packs) { +@@ -31,9 +_,14 @@ + } + + public void reload() { ++ // Paper start - add pendingReload flag to determine required pack loading ++ this.reload(false); ++ } ++ public void reload(final boolean pendingReload) { ++ // Paper end - add pendingReload flag to determine required pack loading + List list = this.selected.stream().map(Pack::getId).collect(ImmutableList.toImmutableList()); + this.available = this.discoverAvailable(); +- this.selected = this.rebuildSelected(list); ++ this.selected = this.rebuildSelected(list, pendingReload); // Paper - add pendingReload flag to determine required pack loading + } + + private Map discoverAvailable() { +@@ -43,16 +_,23 @@ + repositorySource.loadPacks(pack -> map.put(pack.getId(), pack)); + } + +- return ImmutableMap.copyOf(map); ++ // Paper start - custom plugin-loaded datapacks ++ final io.papermc.paper.datapack.PaperDatapackRegistrar registrar = new io.papermc.paper.datapack.PaperDatapackRegistrar(this.validator, map); ++ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callStaticRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.DATAPACK_DISCOVERY, ++ registrar, ++ io.papermc.paper.plugin.bootstrap.BootstrapContext.class ++ ); ++ return ImmutableMap.copyOf(registrar.discoveredPacks); ++ // Paper end - custom plugin-loaded datapacks + } + + public boolean isAbleToClearAnyPack() { +- List list = this.rebuildSelected(List.of()); ++ List list = this.rebuildSelected(List.of(), false); // Paper - add pendingReload flag to determine required pack loading + return !this.selected.equals(list); + } + +- public void setSelected(Collection ids) { +- this.selected = this.rebuildSelected(ids); ++ public void setSelected(Collection ids, final boolean pendingReload) { // Paper - add pendingReload flag to determine required pack loading ++ this.selected = this.rebuildSelected(ids, pendingReload); // Paper - add pendingReload flag to determine required pack loading + } + + public boolean addPack(String id) { +@@ -79,11 +_,11 @@ + } + } + +- private List rebuildSelected(Collection ids) { ++ private List rebuildSelected(Collection ids, boolean pendingReload) { // Paper - add pendingReload flag to determine required pack loading + List list = this.getAvailablePacks(ids).collect(Util.toMutableList()); + + for (Pack pack : this.available.values()) { +- if (pack.isRequired() && !list.contains(pack)) { ++ if (pack.isRequired() && !list.contains(pack) && pendingReload) { // Paper - add pendingReload flag to determine required pack loading + pack.getDefaultPosition().insert(list, pack, Pack::selectionConfig, false); + } + } diff --git a/paper-server/patches/sources/net/minecraft/server/packs/repository/ServerPacksSource.java.patch b/paper-server/patches/sources/net/minecraft/server/packs/repository/ServerPacksSource.java.patch index dc3a6578a..ee3142e9b 100644 --- a/paper-server/patches/sources/net/minecraft/server/packs/repository/ServerPacksSource.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/packs/repository/ServerPacksSource.java.patch @@ -9,7 +9,7 @@ .applyDevelopmentConfig() .pushJarResources() .build(VANILLA_PACK_INFO); -@@ -68,7 +_,18 @@ +@@ -68,15 +_,26 @@ @Nullable @Override protected Pack createBuiltinPack(String id, Pack.ResourcesSupplier resources, Component title) { @@ -29,3 +29,13 @@ } public static PackRepository createPackRepository(Path folder, DirectoryValidator validator) { +- return new PackRepository(new ServerPacksSource(validator), new FolderRepositorySource(folder, PackType.SERVER_DATA, PackSource.WORLD, validator)); ++ return new PackRepository(validator, new ServerPacksSource(validator), new FolderRepositorySource(folder, PackType.SERVER_DATA, PackSource.WORLD, validator)); // Paper - add validator + } + + public static PackRepository createVanillaTrustedRepository() { +- return new PackRepository(new ServerPacksSource(new DirectoryValidator(path -> true))); ++ return new PackRepository(new DirectoryValidator(path -> true), new ServerPacksSource(new DirectoryValidator(path -> true))); // Paper - add validator + } + + public static PackRepository createPackRepository(LevelStorageSource.LevelStorageAccess level) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch index f1de578de..780a51d40 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch @@ -706,52 +706,44 @@ public void awardKillScore(Entity entity, DamageSource damageSource) { if (entity instanceof ServerPlayer) { -@@ -1752,34 +_,70 @@ +@@ -1752,15 +_,22 @@ } public boolean saveAsPassenger(CompoundTag compound) { +- if (this.removalReason != null && !this.removalReason.shouldSave()) { + // CraftBukkit start - allow excluding certain data when saving -+ return this.saveAsPassenger(compound, true); ++ // Paper start - Raw entity serialization API ++ return this.saveAsPassenger(compound, true, false, false); + } -+ -+ public boolean saveAsPassenger(CompoundTag compound, boolean includeAll) { ++ public boolean saveAsPassenger(CompoundTag compound, boolean includeAll, boolean includeNonSaveable, boolean forceSerialization) { ++ // Paper end - Raw entity serialization API + // CraftBukkit end - if (this.removalReason != null && !this.removalReason.shouldSave()) { ++ if (this.removalReason != null && !this.removalReason.shouldSave() && !forceSerialization) { // Paper - Raw entity serialization API return false; } else { - String encodeId = this.getEncodeId(); +- String encodeId = this.getEncodeId(); - if (encodeId == null) { -+ if (!this.persist || encodeId == null) { // CraftBukkit - persist flag ++ String encodeId = this.getEncodeId(includeNonSaveable); // Paper - Raw entity serialization API ++ if ((!this.persist && !forceSerialization) || encodeId == null) { // CraftBukkit - persist flag // Paper - Raw entity serialization API return false; } else { compound.putString("id", encodeId); - this.saveWithoutId(compound); -+ this.saveWithoutId(compound, includeAll); // CraftBukkit - pass on includeAll ++ this.saveWithoutId(compound, includeAll, includeNonSaveable, forceSerialization); // CraftBukkit - pass on includeAll // Paper - Raw entity serialization API return true; } } - } -+ -+ // Paper start - Entity serialization api -+ public boolean serializeEntity(CompoundTag compound) { -+ List pass = new java.util.ArrayList<>(this.getPassengers()); -+ this.passengers = ImmutableList.of(); -+ boolean result = save(compound); -+ this.passengers = ImmutableList.copyOf(pass); -+ return result; -+ } -+ // Paper end - Entity serialization api - - public boolean save(CompoundTag compound) { - return !this.isPassenger() && this.saveAsPassenger(compound); +@@ -1771,15 +_,37 @@ } public CompoundTag saveWithoutId(CompoundTag compound) { + // CraftBukkit start - allow excluding certain data when saving -+ return this.saveWithoutId(compound, true); ++ // Paper start - Raw entity serialization API ++ return this.saveWithoutId(compound, true, false, false); + } + -+ public CompoundTag saveWithoutId(CompoundTag compound, boolean includeAll) { ++ public CompoundTag saveWithoutId(CompoundTag compound, boolean includeAll, boolean includeNonSaveable, boolean forceSerialization) { ++ // Paper end - Raw entity serialization API + // CraftBukkit end try { - if (this.vehicle != null) { @@ -827,7 +819,7 @@ for (Entity entity : this.getPassengers()) { CompoundTag compoundTag = new CompoundTag(); - if (entity.saveAsPassenger(compoundTag)) { -+ if (entity.saveAsPassenger(compoundTag, includeAll)) { // CraftBukkit - pass on includeAll ++ if (entity.saveAsPassenger(compoundTag, includeAll, includeNonSaveable, forceSerialization)) { // CraftBukkit - pass on includeAll // Paper - Raw entity serialization API listTag.add(compoundTag); } } @@ -935,19 +927,30 @@ } catch (Throwable var17) { CrashReport crashReport = CrashReport.forThrowable(var17, "Loading entity NBT"); CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being loaded"); -@@ -1949,6 +_,12 @@ - return type.canSerialize() && key != null ? key.toString() : null; - } +@@ -1944,10 +_,21 @@ + @Nullable + public final String getEncodeId() { ++ // Paper start - Raw entity serialization API ++ return getEncodeId(false); ++ } ++ public final @Nullable String getEncodeId(boolean includeNonSaveable) { ++ // Paper end - Raw entity serialization API + EntityType type = this.getType(); + ResourceLocation key = EntityType.getKey(type); +- return type.canSerialize() && key != null ? key.toString() : null; +- } ++ return (type.canSerialize() || includeNonSaveable) && key != null ? key.toString() : null; // Paper - Raw entity serialization API ++ } ++ + // CraftBukkit start - allow excluding certain data when saving + protected void addAdditionalSaveData(CompoundTag tag, boolean includeAll) { + this.addAdditionalSaveData(tag); + } + // CraftBukkit end -+ + protected abstract void readAdditionalSaveData(CompoundTag tag); - protected abstract void addAdditionalSaveData(CompoundTag tag); @@ -1990,11 +_,61 @@ @Nullable diff --git a/paper-server/patches/sources/net/minecraft/world/entity/LightningBolt.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/LightningBolt.java.patch index ffaffc46d..b5b67ef4e 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/LightningBolt.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/LightningBolt.java.patch @@ -48,7 +48,12 @@ List entities = this.level() .getEntities( this, -@@ -172,22 +_,30 @@ +@@ -168,26 +_,34 @@ + } + + private void spawnFire(int extraIgnitions) { +- if (!this.visualOnly && this.level() instanceof ServerLevel serverLevel && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) { ++ if (!this.visualOnly && !this.isEffect && this.level() instanceof ServerLevel serverLevel && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) { // Paper - prevent any fire if only effect BlockPos blockPos = this.blockPosition(); BlockState state = BaseFireBlock.getState(this.level(), blockPos); if (this.level().getBlockState(blockPos).isAir() && state.canSurvive(this.level(), blockPos)) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch index 4d20a5178..83e1b14b2 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch @@ -112,16 +112,21 @@ this.removeEntitiesOnShoulder(); } } -@@ -717,6 +_,13 @@ +@@ -717,6 +_,18 @@ @Nullable public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean includeThrowerName) { + // CraftBukkit start - SPIGOT-2942: Add boolean to call event -+ return this.drop(droppedItem, dropAround, includeThrowerName, true); ++ return this.drop(droppedItem, dropAround, includeThrowerName, true, null); + } + + @Nullable + public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean includeThrowerName, boolean callEvent) { ++ return this.drop(droppedItem, dropAround, includeThrowerName, callEvent, null); ++ } ++ ++ @Nullable ++ public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean includeThrowerName, boolean callEvent, @Nullable java.util.function.Consumer entityOperation) { + // CraftBukkit end if (!droppedItem.isEmpty() && this.level().isClientSide) { this.swing(InteractionHand.MAIN_HAND); diff --git a/paper-server/patches/sources/net/minecraft/world/level/BlockGetter.java.patch b/paper-server/patches/sources/net/minecraft/world/level/BlockGetter.java.patch index f32901905..41d701302 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/BlockGetter.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/BlockGetter.java.patch @@ -34,9 +34,9 @@ - BlockState blockState = this.getBlockState(traversePos); - FluidState fluidState = this.getFluidState(traversePos); + // CraftBukkit start - moved block handling into separate method for use by Block#rayTrace -+ default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) { ++ default BlockHitResult clip(ClipContext traverseContext, BlockPos traversePos) { + // Paper start - Add predicate for blocks when raytracing -+ return clip(raytrace1, blockposition, null); ++ return clip(traverseContext, traversePos, null); + } + + default BlockHitResult clip(ClipContext traverseContext, BlockPos traversePos, java.util.function.Predicate canCollide) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch index ad2f90861..f56e52852 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BedBlock.java.patch @@ -5,11 +5,11 @@ } - if (!canSetSpawn(level)) { -+ if (false && !canSetSpawn(level)) { // CraftBukkit - moved world and biome check into EntityHuman ++ if (false && !canSetSpawn(level)) { // CraftBukkit - moved world and biome check into Player level.removeBlock(pos, false); BlockPos blockPos = pos.relative(state.getValue(FACING).getOpposite()); if (level.getBlockState(blockPos).is(this)) { -@@ -103,22 +_,62 @@ +@@ -103,22 +_,56 @@ level.explode(null, level.damageSources().badRespawnPointExplosion(center), null, center, 5.0F, true, Level.ExplosionInteraction.BLOCK); return InteractionResult.SUCCESS_SERVER; } else if (state.getValue(OCCUPIED)) { @@ -21,20 +21,20 @@ return InteractionResult.SUCCESS_SERVER; } else { + // CraftBukkit start -+ BlockState finaliblockdata = state; -+ BlockPos finalblockposition = pos; ++ final BlockState finalBlockState = state; ++ final BlockPos finalBlockPos = pos; + // CraftBukkit end player.startSleepInBed(pos).ifLeft(bedSleepingProblem -> { + // Paper start - PlayerBedFailEnterEvent + if (bedSleepingProblem != null) { -+ io.papermc.paper.event.player.PlayerBedFailEnterEvent event = new io.papermc.paper.event.player.PlayerBedFailEnterEvent((org.bukkit.entity.Player) player.getBukkitEntity(), io.papermc.paper.event.player.PlayerBedFailEnterEvent.FailReason.values()[bedSleepingProblem.ordinal()], org.bukkit.craftbukkit.block.CraftBlock.at(level, finalblockposition), !level.dimensionType().bedWorks(), io.papermc.paper.adventure.PaperAdventure.asAdventure(bedSleepingProblem.getMessage())); ++ io.papermc.paper.event.player.PlayerBedFailEnterEvent event = new io.papermc.paper.event.player.PlayerBedFailEnterEvent((org.bukkit.entity.Player) player.getBukkitEntity(), io.papermc.paper.event.player.PlayerBedFailEnterEvent.FailReason.values()[bedSleepingProblem.ordinal()], org.bukkit.craftbukkit.block.CraftBlock.at(level, finalBlockPos), !level.dimensionType().bedWorks(), io.papermc.paper.adventure.PaperAdventure.asAdventure(bedSleepingProblem.getMessage())); + if (!event.callEvent()) { + return; + } + // Paper end - PlayerBedFailEnterEvent + // CraftBukkit start - handling bed explosion from below here + if (event.getWillExplode()) { // Paper - PlayerBedFailEnterEvent -+ this.explodeBed(finaliblockdata, level, finalblockposition); ++ this.explodeBed(finalBlockState, level, finalBlockPos); + } else + // CraftBukkit end if (bedSleepingProblem.getMessage() != null) { @@ -49,24 +49,18 @@ } } -+ // CraftBukkit start -+ private InteractionResult explodeBed(BlockState iblockdata, Level world, BlockPos blockposition) { -+ { -+ { -+ org.bukkit.block.BlockState blockState = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockposition).getState(); // CraftBukkit - capture BlockState before remove block -+ world.removeBlock(blockposition, false); -+ BlockPos blockposition1 = blockposition.relative(((Direction) iblockdata.getValue(BedBlock.FACING)).getOpposite()); ++ // CraftBukkit start - Copied from the above method ++ private InteractionResult explodeBed(BlockState state, Level level, BlockPos pos) { ++ org.bukkit.block.BlockState blockState = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos).getState(); // CraftBukkit - capture BlockState before remove block ++ level.removeBlock(pos, false); ++ BlockPos blockPos = pos.relative(state.getValue(FACING).getOpposite()); ++ if (level.getBlockState(blockPos).is(this)) { ++ level.removeBlock(blockPos, false); ++ } + -+ if (world.getBlockState(blockposition1).getBlock() == this) { -+ world.removeBlock(blockposition1, false); -+ } -+ -+ Vec3 vec3d = blockposition.getCenter(); -+ -+ world.explode(null, world.damageSources().badRespawnPointExplosion(vec3d, blockState), null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state -+ return InteractionResult.SUCCESS; -+ } -+ } ++ Vec3 center = pos.getCenter(); ++ level.explode(null, level.damageSources().badRespawnPointExplosion(center, blockState), null, center, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state ++ return InteractionResult.SUCCESS_SERVER; + } + // CraftBukkit end + diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch index 11a7f5541..f3f15caba 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BigDripleafBlock.java.patch @@ -19,8 +19,8 @@ - this.setTiltAndScheduleTick(state, level, pos, Tilt.UNSTABLE, null); + // CraftBukkit start - tilt dripleaf + org.bukkit.event.Cancellable cancellable; -+ if (entity instanceof net.minecraft.world.entity.player.Player) { -+ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((net.minecraft.world.entity.player.Player) entity, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null); ++ if (entity instanceof net.minecraft.world.entity.player.Player player) { ++ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(player, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null); + } else { + cancellable = new org.bukkit.event.entity.EntityInteractEvent(entity.getBukkitEntity(), level.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ())); + level.getCraftServer().getPluginManager().callEvent((org.bukkit.event.entity.EntityInteractEvent) cancellable); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/Block.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/Block.java.patch index e58b07c7a..1fe47eae6 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/Block.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/Block.java.patch @@ -133,7 +133,7 @@ + return 0; // CraftBukkit + } + // CraftBukkit start -+ public int getExpDrop(BlockState iblockdata, ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, boolean flag) { ++ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { + return 0; + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/BushBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/BushBlock.java.patch index 709e307ca..377f1220e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/BushBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/BushBlock.java.patch @@ -1,13 +1,5 @@ --- a/net/minecraft/world/level/block/BushBlock.java +++ b/net/minecraft/world/level/block/BushBlock.java -@@ -6,6 +_,7 @@ - import net.minecraft.tags.BlockTags; - import net.minecraft.util.RandomSource; - import net.minecraft.world.level.BlockGetter; -+import net.minecraft.world.level.Level; - import net.minecraft.world.level.LevelReader; - import net.minecraft.world.level.ScheduledTickAccess; - import net.minecraft.world.level.block.state.BlockBehaviour; @@ -35,9 +_,15 @@ BlockState neighborState, RandomSource random @@ -18,7 +10,7 @@ + // CraftBukkit start + if (!state.canSurvive(level, pos)) { + // Suppress during worldgen -+ if (!(level instanceof net.minecraft.server.level.ServerLevel world1 && world1.hasPhysicsEvent) || !org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPhysicsEvent(world1, pos).isCancelled()) { // Paper ++ if (!(level instanceof net.minecraft.server.level.ServerLevel serverLevel && serverLevel.hasPhysicsEvent) || !org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPhysicsEvent(serverLevel, pos).isCancelled()) { // Paper + return Blocks.AIR.defaultBlockState(); + } + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch index d564317a0..f3add7001 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch @@ -29,7 +29,7 @@ - player.getFoodData().eat(2, 0.1F); - int bitesValue = state.getValue(BITES); + // CraftBukkit start -+ // entityhuman.getFoodData().eat(2, 0.1F); ++ // player.getFoodData().eat(2, 0.1F); + int oldFoodLevel = player.getFoodData().foodLevel; + + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(player, 2 + oldFoodLevel); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch index 6355a6f4b..f24e49e93 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CaveVines.java.patch @@ -10,8 +10,8 @@ + return InteractionResult.SUCCESS; + } + -+ if (entity instanceof net.minecraft.world.entity.player.Player) { -+ org.bukkit.event.player.PlayerHarvestBlockEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerHarvestBlockEvent(level, pos, (net.minecraft.world.entity.player.Player) entity, net.minecraft.world.InteractionHand.MAIN_HAND, java.util.Collections.singletonList(new ItemStack(Items.GLOW_BERRIES, 1))); ++ if (entity instanceof net.minecraft.world.entity.player.Player player) { ++ org.bukkit.event.player.PlayerHarvestBlockEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerHarvestBlockEvent(level, pos, player, net.minecraft.world.InteractionHand.MAIN_HAND, java.util.Collections.singletonList(new ItemStack(Items.GLOW_BERRIES, 1))); + if (event.isCancelled()) { + return InteractionResult.SUCCESS; // We need to return a success either way, because making it PASS or FAIL will result in a bug where cancelling while harvesting w/ block in hand places block + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch index 2f9e73d10..1306f54ed 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ComposterBlock.java.patch @@ -39,8 +39,8 @@ public static BlockState extractProduce(Entity entity, BlockState state, Level level, BlockPos pos) { + // CraftBukkit start + if (entity != null && !(entity instanceof Player)) { -+ BlockState iblockdata1 = ComposterBlock.empty(entity, state, org.bukkit.craftbukkit.util.DummyGeneratorAccess.INSTANCE, pos); -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, iblockdata1)) { ++ BlockState emptyState = ComposterBlock.empty(entity, state, org.bukkit.craftbukkit.util.DummyGeneratorAccess.INSTANCE, pos); ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, emptyState)) { + return state; + } + } @@ -100,9 +100,9 @@ public static class EmptyContainer extends SimpleContainer implements WorldlyContainer { - public EmptyContainer() { -+ public EmptyContainer(LevelAccessor generatoraccess, BlockPos blockposition) { // CraftBukkit ++ public EmptyContainer(LevelAccessor levelAccessor, BlockPos blockPos) { // CraftBukkit super(0); -+ this.bukkitOwner = new org.bukkit.craftbukkit.inventory.CraftBlockInventoryHolder(generatoraccess, blockposition, this); // CraftBukkit ++ this.bukkitOwner = new org.bukkit.craftbukkit.inventory.CraftBlockInventoryHolder(levelAccessor, blockPos, this); // CraftBukkit } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CrafterBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CrafterBlock.java.patch index b592d3f85..fb9ecec88 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CrafterBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CrafterBlock.java.patch @@ -31,8 +31,8 @@ + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly -+ if (containerAt instanceof CompoundContainer) { -+ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) containerAt); ++ if (containerAt instanceof CompoundContainer compoundContainer) { ++ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); + } else { + destinationInventory = containerAt.getOwner().getInventory(); + } @@ -57,8 +57,8 @@ + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly -+ if (containerAt instanceof CompoundContainer) { -+ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) containerAt); ++ if (containerAt instanceof CompoundContainer compoundContainer) { ++ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); + } else { + destinationInventory = containerAt.getOwner().getInventory(); + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DropExperienceBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DropExperienceBlock.java.patch index 1487606ce..6fbb433e7 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DropExperienceBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DropExperienceBlock.java.patch @@ -4,16 +4,15 @@ @Override protected void spawnAfterBreak(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { super.spawnAfterBreak(state, level, pos, stack, dropExperience); -- if (dropExperience) { -- this.tryDropExperience(level, pos, stack, this.xpRange); -- } + // CraftBukkit start - Delegate to getExpDrop + } + + @Override -+ public int getExpDrop(BlockState iblockdata, ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, boolean flag) { -+ if (flag) { -+ return this.tryDropExperience(worldserver, blockposition, itemstack, this.xpRange); ++ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { + if (dropExperience) { +- this.tryDropExperience(level, pos, stack, this.xpRange); +- } ++ return this.tryDropExperience(level, pos, stack, this.xpRange); + } + + return 0; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/DropperBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/DropperBlock.java.patch index 1b70c5b62..bcab2da23 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/DropperBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/DropperBlock.java.patch @@ -39,8 +39,8 @@ + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly -+ if (containerAt instanceof CompoundContainer) { -+ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) containerAt); ++ if (containerAt instanceof CompoundContainer compoundContainer) { ++ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); + } else { + destinationInventory = containerAt.getOwner().getInventory(); + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch index 82a4a56c8..30b8c15a5 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/EnderChestBlock.java +++ b/net/minecraft/world/level/block/EnderChestBlock.java -@@ -78,16 +_,15 @@ +@@ -78,16 +_,17 @@ PlayerEnderChestContainer enderChestInventory = player.getEnderChestInventory(); if (enderChestInventory != null && level.getBlockEntity(pos) instanceof EnderChestBlockEntity enderChestBlockEntity) { BlockPos blockPos = pos.above(); @@ -18,7 +18,9 @@ + // Paper start - Fix InventoryOpenEvent cancellation - moved up; + enderChestInventory.setActiveChest(enderChestBlockEntity); // Needs to happen before ChestMenu.threeRows as it is required for opening animations + if (level instanceof ServerLevel serverLevel && player.openMenu( -+ new SimpleMenuProvider((i, inventory, playerx) -> ChestMenu.threeRows(i, inventory, enderChestInventory), CONTAINER_TITLE) ++ new SimpleMenuProvider( ++ (containerId, playerInventory, player1) -> ChestMenu.threeRows(containerId, playerInventory, enderChestInventory), CONTAINER_TITLE ++ ) + ).isPresent()) { + // Paper end - Fix InventoryOpenEvent cancellation - moved up; player.awardStat(Stats.OPEN_ENDERCHEST); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/FireBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/FireBlock.java.patch index ebd1c8425..3d286f8b4 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/FireBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/FireBlock.java.patch @@ -1,13 +1,5 @@ --- a/net/minecraft/world/level/block/FireBlock.java +++ b/net/minecraft/world/level/block/FireBlock.java -@@ -14,6 +_,7 @@ - import net.minecraft.tags.BiomeTags; - import net.minecraft.util.RandomSource; - import net.minecraft.world.item.context.BlockPlaceContext; -+import net.minecraft.world.item.context.UseOnContext; - import net.minecraft.world.level.BlockGetter; - import net.minecraft.world.level.GameRules; - import net.minecraft.world.level.Level; @@ -122,7 +_,25 @@ BlockState neighborState, RandomSource random @@ -40,7 +32,7 @@ @Override protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { - level.scheduleTick(pos, this, getFireTickDelay(level.random)); -+ level.scheduleTick(pos, (Block) this, FireBlock.getFireTickDelay(level)); // Paper - Add fire-tick-delay option ++ level.scheduleTick(pos, this, getFireTickDelay(level)); // Paper - Add fire-tick-delay option if (level.getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) { if (!state.canSurvive(level, pos)) { - level.removeBlock(pos, false); @@ -154,7 +146,7 @@ - protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) { - super.onPlace(state, level, pos, oldState, isMoving); - level.scheduleTick(pos, this, getFireTickDelay(level.random)); -+ protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving, UseOnContext context) { ++ protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving, net.minecraft.world.item.context.UseOnContext context) { + super.onPlace(state, level, pos, oldState, isMoving, context); + // CraftBukkit end + level.scheduleTick(pos, this, FireBlock.getFireTickDelay(level)); // Paper - Add fire-tick-delay option @@ -162,8 +154,8 @@ - private static int getFireTickDelay(RandomSource random) { - return 30 + random.nextInt(10); -+ private static int getFireTickDelay(Level world) { // Paper - Add fire-tick-delay option -+ return world.paperConfig().environment.fireTickDelay + world.random.nextInt(10); // Paper - Add fire-tick-delay option ++ private static int getFireTickDelay(Level level) { // Paper - Add fire-tick-delay option ++ return level.paperConfig().environment.fireTickDelay + level.random.nextInt(10); // Paper - Add fire-tick-delay option } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LayeredCauldronBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LayeredCauldronBlock.java.patch index f4af9face..6d49c999f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LayeredCauldronBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LayeredCauldronBlock.java.patch @@ -41,33 +41,33 @@ + // CraftBukkit start + LayeredCauldronBlock.lowerFillLevel(state, level, pos, null, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason.UNKNOWN); + } -+ public static boolean lowerFillLevel(BlockState state, Level level, BlockPos BlockPos, Entity entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason reason) { ++ public static boolean lowerFillLevel(BlockState state, Level level, BlockPos pos, Entity entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason reason) { + int i = (Integer) state.getValue(LayeredCauldronBlock.LEVEL) - 1; + BlockState iblockdata1 = i == 0 ? Blocks.CAULDRON.defaultBlockState() : (BlockState) state.setValue(LayeredCauldronBlock.LEVEL, i); + -+ return LayeredCauldronBlock.changeLevel(level, BlockPos, iblockdata1, entity, reason); ++ return LayeredCauldronBlock.changeLevel(level, pos, iblockdata1, entity, reason); + } + + // CraftBukkit start + // Paper start - Call CauldronLevelChangeEvent -+ public static boolean changeLevel(Level world, BlockPos pos, BlockState newBlock, @javax.annotation.Nullable Entity entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason reason) { // Paper - entity is nullable -+ return changeLevel(world, pos, newBlock, entity, reason, true); ++ public static boolean changeLevel(Level level, BlockPos pos, BlockState newBlock, @javax.annotation.Nullable Entity entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason reason) { // Paper - entity is nullable ++ return changeLevel(level, pos, newBlock, entity, reason, true); + } + -+ public static boolean changeLevel(Level world, BlockPos pos, BlockState newBlock, @javax.annotation.Nullable Entity entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason reason, boolean sendGameEvent) { // Paper - entity is nullable ++ public static boolean changeLevel(Level level, BlockPos pos, BlockState newBlock, @javax.annotation.Nullable Entity entity, org.bukkit.event.block.CauldronLevelChangeEvent.ChangeReason reason, boolean sendGameEvent) { // Paper - entity is nullable + // Paper end - Call CauldronLevelChangeEvent -+ org.bukkit.craftbukkit.block.CraftBlockState newState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(world, pos); ++ org.bukkit.craftbukkit.block.CraftBlockState newState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(level, pos); + newState.setData(newBlock); + + org.bukkit.event.block.CauldronLevelChangeEvent event = new org.bukkit.event.block.CauldronLevelChangeEvent( -+ org.bukkit.craftbukkit.block.CraftBlock.at(world, pos), ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), + (entity == null) ? null : entity.getBukkitEntity(), reason, newState + ); + if (!event.callEvent()) { + return false; + } + newState.update(true); -+ if (sendGameEvent) world.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newBlock)); // Paper - Call CauldronLevelChangeEvent ++ if (sendGameEvent) level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newBlock)); // Paper - Call CauldronLevelChangeEvent + return true; + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LightBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LightBlock.java.patch index c9fda343b..8b3d19d59 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LightBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LightBlock.java.patch @@ -6,9 +6,9 @@ } + // Paper start - prevent unintended light block manipulation + @Override -+ protected InteractionResult useItemOn(ItemStack stack, BlockState state, Level world, BlockPos pos, Player player, net.minecraft.world.InteractionHand hand, BlockHitResult hit) { -+ if (player.getItemInHand(hand).getItem() != Items.LIGHT || (world instanceof final net.minecraft.server.level.ServerLevel serverLevel && !player.mayInteract(serverLevel, pos)) || !player.mayUseItemAt(pos, hit.getDirection(), player.getItemInHand(hand))) { return net.minecraft.world.InteractionResult.PASS; } // Paper - Prevent unintended light block manipulation -+ return super.useItemOn(stack, state, world, pos, player, hand, hit); ++ protected InteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, net.minecraft.world.InteractionHand hand, BlockHitResult hitResult) { ++ if (player.getItemInHand(hand).getItem() != Items.LIGHT || (level instanceof final net.minecraft.server.level.ServerLevel serverLevel && !player.mayInteract(serverLevel, pos)) || !player.mayUseItemAt(pos, hitResult.getDirection(), player.getItemInHand(hand))) { return net.minecraft.world.InteractionResult.PASS; } // Paper - Prevent unintended light block manipulation ++ return super.useItemOn(stack, state, level, pos, player, hand, hitResult); + } + // Paper end - prevent unintended light block manipulation diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch index 35dc60692..4450068d1 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/LiquidBlock.java.patch @@ -11,21 +11,21 @@ + } + } + // Paper start - Configurable speed for water flowing over lava -+ public int getFlowSpeed(Level world, BlockPos blockposition) { ++ public int getFlowSpeed(Level level, BlockPos pos) { + if (net.minecraft.core.registries.BuiltInRegistries.FLUID.wrapAsHolder(this.fluid).is(FluidTags.WATER)) { + if ( -+ isLava(world, blockposition.north(1)) || -+ isLava(world, blockposition.south(1)) || -+ isLava(world, blockposition.west(1)) || -+ isLava(world, blockposition.east(1)) ++ isLava(level, pos.north(1)) || ++ isLava(level, pos.south(1)) || ++ isLava(level, pos.west(1)) || ++ isLava(level, pos.east(1)) + ) { -+ return world.paperConfig().environment.waterOverLavaFlowSpeed; ++ return level.paperConfig().environment.waterOverLavaFlowSpeed; + } + } -+ return this.fluid.getTickDelay(world); ++ return this.fluid.getTickDelay(level); + } -+ private static boolean isLava(Level world, BlockPos blockPos) { -+ final FluidState fluidState = world.getFluidIfLoaded(blockPos); ++ private static boolean isLava(Level level, BlockPos pos) { ++ final FluidState fluidState = level.getFluidIfLoaded(pos); + return fluidState != null && fluidState.is(FluidTags.LAVA); + } + // Paper end - Configurable speed for water flowing over lava diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch index 0ff72778c..76965a2ac 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch @@ -29,20 +29,6 @@ } level.setBlock(pos, state.setValue(POWERED, Boolean.valueOf(hasNeighborSignal)), 3); -@@ -104,6 +_,13 @@ - - private void playNote(@Nullable Entity entity, BlockState state, Level level, BlockPos pos) { - if (state.getValue(INSTRUMENT).worksAboveNoteBlock() || level.getBlockState(pos.above()).isAir()) { -+ // CraftBukkit start -+ // org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(NoteBlock.INSTRUMENT), state.getValue(NoteBlock.NOTE)); -+ // if (event.isCancelled()) { -+ // return; -+ // } -+ // CraftBukkit end -+ // Paper - move NotePlayEvent call to fix instrument/note changes; TODO any way to cancel the game event? - level.blockEvent(pos, this, 0, 0); - level.gameEvent(entity, GameEvent.NOTE_BLOCK_PLAY, pos); - } @@ -121,7 +_,7 @@ @Override protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/PressurePlateBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/PressurePlateBlock.java.patch index 235e99adf..a28da6fbd 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/PressurePlateBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/PressurePlateBlock.java.patch @@ -20,8 +20,8 @@ + org.bukkit.plugin.PluginManager manager = level.getCraftServer().getPluginManager(); + org.bukkit.event.Cancellable cancellable; + -+ if (entity instanceof Player) { -+ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((Player) entity, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null); ++ if (entity instanceof Player player) { ++ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(player, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null); + } else { + cancellable = new org.bukkit.event.entity.EntityInteractEvent(entity.getBukkitEntity(), bworld.getBlockAt(pos.getX(), pos.getY(), pos.getZ())); + manager.callEvent((org.bukkit.event.entity.EntityInteractEvent) cancellable); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/RedStoneOreBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/RedStoneOreBlock.java.patch index 6f93bcddd..c2c101fbf 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/RedStoneOreBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/RedStoneOreBlock.java.patch @@ -14,8 +14,8 @@ if (!entity.isSteppingCarefully()) { - interact(state, level, pos); + // CraftBukkit start -+ if (entity instanceof Player) { -+ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((Player) entity, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null); ++ if (entity instanceof Player player) { ++ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(player, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null); + if (!event.isCancelled()) { + RedStoneOreBlock.interact(level.getBlockState(pos), level, pos, entity); // add entity + } @@ -71,15 +71,14 @@ @Override protected void spawnAfterBreak(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { super.spawnAfterBreak(state, level, pos, stack, dropExperience); -- if (dropExperience) { -- this.tryDropExperience(level, pos, stack, UniformInt.of(1, 5)); + // CraftBukkit start - Delegated to getExpDrop + } + + @Override -+ public int getExpDrop(BlockState iblockdata, ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, boolean flag) { -+ if (flag) { -+ return this.tryDropExperience(worldserver, blockposition, itemstack, UniformInt.of(1, 5)); ++ public int getExpDrop(BlockState state, ServerLevel level, BlockPos pos, ItemStack stack, boolean dropExperience) { + if (dropExperience) { +- this.tryDropExperience(level, pos, stack, UniformInt.of(1, 5)); ++ return this.tryDropExperience(level, pos, stack, UniformInt.of(1, 5)); } + + return 0; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SnifferEggBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SnifferEggBlock.java.patch index aa5c5e281..dc26f22de 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/SnifferEggBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/SnifferEggBlock.java.patch @@ -5,9 +5,9 @@ } + // Paper start - Call BlockFadeEvent -+ private void rescheduleTick(ServerLevel world, BlockPos pos) { -+ int baseDelay = hatchBoost(world, pos) ? world.paperConfig().entities.sniffer.boostedHatchTime.or(BOOSTED_HATCH_TIME_TICKS) : world.paperConfig().entities.sniffer.hatchTime.or(REGULAR_HATCH_TIME_TICKS); // Paper - Configure sniffer egg hatch time -+ world.scheduleTick(pos, this, (baseDelay / 3) + world.random.nextInt(RANDOM_HATCH_OFFSET_TICKS)); ++ private void rescheduleTick(ServerLevel level, BlockPos pos) { ++ int baseDelay = hatchBoost(level, pos) ? level.paperConfig().entities.sniffer.boostedHatchTime.or(BOOSTED_HATCH_TIME_TICKS) : level.paperConfig().entities.sniffer.hatchTime.or(REGULAR_HATCH_TIME_TICKS); // Paper - Configure sniffer egg hatch time ++ level.scheduleTick(pos, this, (baseDelay / 3) + level.random.nextInt(RANDOM_HATCH_OFFSET_TICKS)); + // reschedule to avoid being stuck here and behave like the other calls (see #onPlace) + } + // Paper end - Call BlockFadeEvent diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/TripWireHookBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/TripWireHookBlock.java.patch index 661176a03..225f53b41 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/TripWireHookBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/TripWireHookBlock.java.patch @@ -1,21 +1,65 @@ --- a/net/minecraft/world/level/block/TripWireHookBlock.java +++ b/net/minecraft/world/level/block/TripWireHookBlock.java -@@ -173,9 +_,18 @@ +@@ -127,10 +_,10 @@ + if (optionalValue.isPresent()) { + Direction direction = optionalValue.get(); + boolean flag = hookState.getOptionalValue(ATTACHED).orElse(false); +- boolean flag1 = hookState.getOptionalValue(POWERED).orElse(false); ++ boolean flag1 = hookState.getOptionalValue(POWERED).orElse(false); // Paper - diff on change, for event below + Block block = hookState.getBlock(); + boolean flag2 = !attaching; +- boolean flag3 = false; ++ boolean flag3 = false; // Paper - diff on change, for event below + int i = 0; + BlockState[] blockStates = new BlockState[42]; + +@@ -166,21 +_,48 @@ + flag2 &= i > 1; + flag3 &= flag2; + BlockState blockState1 = block.defaultBlockState().trySetValue(ATTACHED, Boolean.valueOf(flag2)).trySetValue(POWERED, Boolean.valueOf(flag3)); ++ boolean cancelledEmitterHook = false, cancelledReceiverHook = false; // Paper - Call BlockRedstoneEvent ++ boolean wasPowered = flag1, willBePowered = flag3; // Paper - OBFHELPER + if (i > 0) { + BlockPos blockPosx = pos.relative(direction, i); ++ // Paper start - Call BlockRedstoneEvent ++ if (wasPowered != willBePowered) { ++ int newCurrent = willBePowered ? 15 : 0; ++ org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPosx), wasPowered ? 15 : 0, newCurrent ++ ); ++ event.callEvent(); ++ cancelledReceiverHook = event.getNewCurrent() != newCurrent; ++ } ++ if (!cancelledReceiverHook) { // always trigger two events even when the first hook current change is cancelled ++ // Paper end - Call BlockRedstoneEvent + Direction opposite = direction.getOpposite(); + level.setBlock(blockPosx, blockState1.setValue(FACING, opposite), 3); notifyNeighbors(block, level, blockPosx, opposite); emitState(level, blockPosx, flag2, flag3, flag, flag1); - } -+ // CraftBukkit start -+ org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), 15, 0); -+ level.getCraftServer().getPluginManager().callEvent(eventRedstone); -+ -+ if (eventRedstone.getNewCurrent() > 0) { -+ return; +- } ++ } // Paper - Call BlockRedstoneEvent + } -+ // CraftBukkit end ++ // Paper start - Call BlockRedstoneEvent ++ if (wasPowered != willBePowered) { ++ int newCurrent = willBePowered ? 15 : 0; ++ org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), wasPowered ? 15 : 0, newCurrent ++ ); ++ event.callEvent(); ++ cancelledEmitterHook = event.getNewCurrent() != newCurrent; ++ } ++ // Paper end - Call BlockRedstoneEvent ++ if (!cancelledEmitterHook) { // Paper - Call BlockRedstoneEvent emitState(level, pos, flag2, flag3, flag, flag1); if (!attaching) { + if (level.getBlockState(pos).is(Blocks.TRIPWIRE_HOOK)) // Paper - Validate tripwire hook placement before update level.setBlock(pos, blockState1.setValue(FACING, direction), 3); if (shouldNotifyNeighbours) { notifyNeighbors(block, level, pos, direction); + } + } ++ } // Paper - Call BlockRedstoneEvent + + if (flag != flag2) { + for (int i2 = 1; i2 < i; i2++) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch index b84d89679..13adcff53 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch @@ -1,11 +1,9 @@ --- a/net/minecraft/world/level/block/entity/SignBlockEntity.java +++ b/net/minecraft/world/level/block/entity/SignBlockEntity.java -@@ -54,11 +_,16 @@ - return new SignText(); +@@ -55,10 +_,15 @@ } -- public boolean isFacingFrontText(Player player) { -+ public boolean isFacingFrontText(net.minecraft.world.entity.player.Player player) { + public boolean isFacingFrontText(Player player) { + // Paper start - More Sign Block API + return this.isFacingFrontText(player.getX(), player.getZ()); + } @@ -15,8 +13,8 @@ Vec3 signHitboxCenterPosition = signBlock.getSignHitboxCenterPosition(this.getBlockState()); - double d = player.getX() - (this.getBlockPos().getX() + signHitboxCenterPosition.x); - double d1 = player.getZ() - (this.getBlockPos().getZ() + signHitboxCenterPosition.z); -+ double d = x - ((double) this.getBlockPos().getX() + signHitboxCenterPosition.x); // Paper - More Sign Block API -+ double d1 = z - ((double) this.getBlockPos().getZ() + signHitboxCenterPosition.z); // Paper - More Sign Block AP ++ double d = x - (this.getBlockPos().getX() + signHitboxCenterPosition.x); // Paper - More Sign Block API ++ double d1 = z - (this.getBlockPos().getZ() + signHitboxCenterPosition.z); // Paper - More Sign Block AP float yRotationDegrees = signBlock.getYRotationDegrees(this.getBlockState()); float f = (float)(Mth.atan2(d1, d) * 180.0F / (float)Math.PI) - 90.0F; return Mth.degreesDifferenceAbs(yRotationDegrees, f) <= 90.0F; @@ -136,14 +134,14 @@ + } + }; + -+ private CommandSourceStack createCommandSourceStack(@Nullable Player player, Level world, BlockPos pos) { ++ private CommandSourceStack createCommandSourceStack(@Nullable Player player, Level level, BlockPos pos) { + // CraftBukkit end String string = player == null ? "Sign" : player.getName().getString(); Component component = (Component)(player == null ? Component.literal("Sign") : player.getDisplayName()); - return new CommandSourceStack(CommandSource.NULL, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel)level, 2, string, component, level.getServer(), player); + + // Paper start - Fix commands from signs not firing command events -+ CommandSource commandSource = world.paperConfig().misc.showSignClickCommandFailureMsgsToPlayer ? new io.papermc.paper.commands.DelegatingCommandSource(this.commandSource) { ++ CommandSource commandSource = level.paperConfig().misc.showSignClickCommandFailureMsgsToPlayer ? new io.papermc.paper.commands.DelegatingCommandSource(this.commandSource) { + @Override + public void sendSystemMessage(Component message) { + if (player instanceof final net.minecraft.server.level.ServerPlayer serverPlayer) { @@ -158,7 +156,7 @@ + } : this.commandSource; + // Paper end - Fix commands from signs not firing command events + // CraftBukkit - this -+ return new CommandSourceStack(commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel) world, 2, string, component, world.getServer(), player); // Paper - Fix commands from signs not firing command events ++ return new CommandSourceStack(commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel)level, 2, string, component, level.getServer(), player); // Paper - Fix commands from signs not firing command events } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch index c28bc3845..89553e4e2 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch @@ -64,9 +64,9 @@ - LoadingCache>> loadingCache = profileCacheById; - return loadingCache != null ? loadingCache.getUnchecked(profileUuid) : CompletableFuture.completedFuture(Optional.empty()); + // Paper start - player profile events -+ public static CompletableFuture> fetchGameProfile(UUID uuid, @Nullable String name) { ++ public static CompletableFuture> fetchGameProfile(UUID profileUuid, @Nullable String name) { + LoadingCache, CompletableFuture>> loadingCache = profileCacheById; -+ return loadingCache != null ? loadingCache.getUnchecked(new com.mojang.datafixers.util.Pair<>(uuid, name != null ? new com.mojang.authlib.GameProfile(uuid, name) : null)) : CompletableFuture.completedFuture(Optional.empty()); ++ return loadingCache != null ? loadingCache.getUnchecked(new com.mojang.datafixers.util.Pair<>(profileUuid, name != null ? new com.mojang.authlib.GameProfile(profileUuid, name) : null)) : CompletableFuture.completedFuture(Optional.empty()); + // Paper end - player profile events } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonBaseBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonBaseBlock.java.patch index 9ee94df16..8f8179f69 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonBaseBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/piston/PistonBaseBlock.java.patch @@ -1,24 +1,5 @@ --- a/net/minecraft/world/level/block/piston/PistonBaseBlock.java +++ b/net/minecraft/world/level/block/piston/PistonBaseBlock.java -@@ -145,6 +_,18 @@ - i = 2; - } - -+ // CraftBukkit start -+ // if (!this.isSticky) { // Paper - Fix sticky pistons and BlockPistonRetractEvent; Move further down -+ // org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); -+ // BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, ImmutableList.of(), CraftBlock.notchToBlockFace(enumdirection)); -+ // world.getCraftServer().getPluginManager().callEvent(event); -+ // -+ // if (event.isCancelled()) { -+ // return; -+ // } -+ // } -+ // PAIL: checkME - what happened to setTypeAndData? -+ // CraftBukkit end - level.blockEvent(pos, this, i, direction.get3DDataValue()); - } - } @@ -174,6 +_,12 @@ @Override protected boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id, int param) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch index ee4aa83de..f9c4ef45c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/state/BlockBehaviour.java.patch @@ -9,8 +9,8 @@ + } + + // CraftBukkit start -+ protected void onPlace(BlockState iblockdata, Level world, BlockPos blockposition, BlockState iblockdata1, boolean flag, @Nullable net.minecraft.world.item.context.UseOnContext context) { -+ this.onPlace(iblockdata, world, blockposition, iblockdata1, flag); ++ protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston, @Nullable net.minecraft.world.item.context.UseOnContext context) { ++ this.onPlace(state, level, pos, oldState, movedByPiston); + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch index d1b576982..f20d2cd0c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkAccess.java.patch @@ -64,27 +64,23 @@ } public abstract ChunkStatus getPersistedStatus(); -@@ -446,6 +_,26 @@ +@@ -446,6 +_,22 @@ throw new ReportedException(crashReport); } } + // CraftBukkit start -+ public void setBiome(int i, int j, int k, Holder biome) { ++ public void setBiome(int x, int y, int z, Holder biome) { + try { -+ int l = QuartPos.fromBlock(this.getMinY()); -+ int i1 = l + QuartPos.fromBlock(this.getHeight()) - 1; -+ int j1 = Mth.clamp(j, l, i1); -+ int k1 = this.getSectionIndex(QuartPos.toBlock(j1)); -+ -+ this.sections[k1].setBiome(i & 3, j1 & 3, k & 3, biome); ++ int minY = QuartPos.fromBlock(this.getMinY()); ++ int maxY = minY + QuartPos.fromBlock(this.getHeight()) - 1; ++ int clampedY = Mth.clamp(y, minY, maxY); ++ int sectionIndex = this.getSectionIndex(QuartPos.toBlock(clampedY)); ++ this.sections[sectionIndex].setBiome(x & 3, clampedY & 3, z & 3, biome); + } catch (Throwable throwable) { -+ CrashReport crashreport = CrashReport.forThrowable(throwable, "Setting biome"); -+ CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Biome being set"); -+ -+ crashreportsystemdetails.setDetail("Location", () -> { -+ return CrashReportCategory.formatLocation(this, i, j, k); -+ }); -+ throw new ReportedException(crashreport); ++ CrashReport report = CrashReport.forThrowable(throwable, "Setting biome"); ++ CrashReportCategory reportCategory = report.addCategory("Biome being set"); ++ reportCategory.setDetail("Location", () -> CrashReportCategory.formatLocation(this, x, y, z)); ++ throw new ReportedException(report); + } + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch index 5fa4572bc..b2e3cf186 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/ChunkGenerator.java.patch @@ -94,24 +94,24 @@ } } + // CraftBukkit start -+ public void applyBiomeDecoration(WorldGenLevel world, ChunkAccess chunk, StructureManager structureAccessor) { -+ this.applyBiomeDecoration(world, chunk, structureAccessor, true); ++ public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { ++ this.applyBiomeDecoration(level, chunk, structureManager, true); + } + -+ public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) { -+ if (vanilla) { -+ this.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); ++ public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager, boolean addVanillaDecorations) { ++ if (addVanillaDecorations) { ++ this.addVanillaDecorations(level, chunk, structureManager); + } + -+ org.bukkit.World world = generatoraccessseed.getMinecraftWorld().getWorld(); ++ org.bukkit.World world = level.getMinecraftWorld().getWorld(); + // only call when a populator is present (prevents unnecessary entity conversion) + if (!world.getPopulators().isEmpty()) { -+ org.bukkit.craftbukkit.generator.CraftLimitedRegion limitedRegion = new org.bukkit.craftbukkit.generator.CraftLimitedRegion(generatoraccessseed, ichunkaccess.getPos()); -+ int x = ichunkaccess.getPos().x; -+ int z = ichunkaccess.getPos().z; ++ org.bukkit.craftbukkit.generator.CraftLimitedRegion limitedRegion = new org.bukkit.craftbukkit.generator.CraftLimitedRegion(level, chunk.getPos()); ++ int x = chunk.getPos().x; ++ int z = chunk.getPos().z; + for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) { -+ WorldgenRandom seededrandom = new WorldgenRandom(new net.minecraft.world.level.levelgen.LegacyRandomSource(generatoraccessseed.getSeed())); -+ seededrandom.setDecorationSeed(generatoraccessseed.getSeed(), x, z); ++ WorldgenRandom seededrandom = new WorldgenRandom(new net.minecraft.world.level.levelgen.LegacyRandomSource(level.getSeed())); ++ seededrandom.setDecorationSeed(level.getSeed(), x, z); + populator.populate(world, new org.bukkit.craftbukkit.util.RandomSourceWrapper.RandomWrapper(seededrandom), x, z, limitedRegion); + } + limitedRegion.saveEntities(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch index dcd413b18..29f06860a 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunk.java.patch @@ -29,7 +29,7 @@ ) { super(pos, data, level, level.registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); - this.level = level; -+ this.level = (net.minecraft.server.level.ServerLevel) level; // CraftBukkit - type ++ this.level = (ServerLevel) level; // CraftBukkit - type this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>(); for (Heightmap.Types types : Heightmap.Types.values()) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch index f4a6ffa6f..96c848d7c 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/LevelChunkSection.java.patch @@ -37,8 +37,8 @@ return this.biomes.get(x, y, z); } + // CraftBukkit start -+ public void setBiome(int i, int j, int k, Holder biome) { -+ this.biomes.set(i, j, k, biome); ++ public void setBiome(int x, int y, int z, Holder biome) { ++ this.biomes.set(x, y, z, biome); + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/ChunkStorage.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/ChunkStorage.java.patch index 9188002f3..666d4d9cb 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/ChunkStorage.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/ChunkStorage.java.patch @@ -1,41 +1,10 @@ --- a/net/minecraft/world/level/chunk/storage/ChunkStorage.java +++ b/net/minecraft/world/level/chunk/storage/ChunkStorage.java -@@ -38,17 +_,63 @@ +@@ -38,17 +_,30 @@ return this.worker.isOldChunkAround(pos, radius); } + // CraftBukkit start -+ private boolean check(net.minecraft.server.level.ServerChunkCache cps, int x, int z) { -+ if (true) return true; // Paper - Perf: this isn't even needed anymore, light is purged updating to 1.14+, why are we holding up the conversion process reading chunk data off disk - return true, we need to set light populated to true so the converter recognizes the chunk as being "full" -+ ChunkPos pos = new ChunkPos(x, z); -+ if (cps != null) { -+ com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); -+ if (cps.hasChunk(x, z)) { -+ return true; -+ } -+ } -+ -+ CompoundTag nbt; -+ try { -+ nbt = this.read(pos).get().orElse(null); -+ } catch (InterruptedException | java.util.concurrent.ExecutionException ex) { -+ throw new RuntimeException(ex); -+ } -+ if (nbt != null) { -+ CompoundTag level = nbt.getCompound("Level"); -+ if (level.getBoolean("TerrainPopulated")) { -+ return true; -+ } -+ -+ net.minecraft.world.level.chunk.status.ChunkStatus status = net.minecraft.world.level.chunk.status.ChunkStatus.byName(level.getString("Status")); -+ if (status != null && status.isOrAfter(net.minecraft.world.level.chunk.status.ChunkStatus.FEATURES)) { -+ return true; -+ } -+ } -+ -+ return false; -+ } -+ public CompoundTag upgradeChunkTag( - ResourceKey levelKey, + ResourceKey levelKey, @@ -44,7 +13,7 @@ - Optional>> chunkGeneratorKey + Optional>> chunkGeneratorKey, + ChunkPos pos, -+ @Nullable net.minecraft.world.level.LevelAccessor generatoraccess ++ @Nullable net.minecraft.world.level.LevelAccessor levelAccessor + // CraftBukkit end ) { int version = getVersion(chunkData); @@ -56,10 +25,8 @@ + if (version < 1466) { + CompoundTag level = chunkData.getCompound("Level"); + if (level.getBoolean("TerrainPopulated") && !level.getBoolean("LightPopulated")) { -+ net.minecraft.server.level.ServerChunkCache cps = (generatoraccess == null) ? null : ((net.minecraft.server.level.ServerLevel) generatoraccess).getChunkSource(); -+ if (this.check(cps, pos.x - 1, pos.z) && this.check(cps, pos.x - 1, pos.z - 1) && this.check(cps, pos.x, pos.z - 1)) { -+ level.putBoolean("LightPopulated", true); -+ } ++ // Light is purged updating to 1.14+. We need to set light populated to true so the converter recognizes the chunk as being "full" ++ level.putBoolean("LightPopulated", true); + } + } + // CraftBukkit end @@ -72,7 +39,7 @@ + // Spigot start - SPIGOT-6806: Quick and dirty way to prevent below zero generation in old chunks, by setting the status to heightmap instead of empty + boolean stopBelowZero = false; -+ boolean belowZeroGenerationInExistingChunks = (generatoraccess != null) ? ((net.minecraft.server.level.ServerLevel) generatoraccess).spigotConfig.belowZeroGenerationInExistingChunks : org.spigotmc.SpigotConfig.belowZeroGenerationInExistingChunks; ++ boolean belowZeroGenerationInExistingChunks = (levelAccessor != null) ? ((net.minecraft.server.level.ServerLevel) levelAccessor).spigotConfig.belowZeroGenerationInExistingChunks : org.spigotmc.SpigotConfig.belowZeroGenerationInExistingChunks; + + if (version <= 2730 && !belowZeroGenerationInExistingChunks) { + stopBelowZero = "full".equals(chunkData.getCompound("Level").getString("Status")); diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFile.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFile.java.patch index 93e36df68..adc8aaa96 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFile.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/RegionFile.java.patch @@ -46,7 +46,7 @@ return false; } } -@@ -331,13 +_,18 @@ +@@ -331,6 +_,11 @@ try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { chunkData.position(5); fileChannel.write(chunkData); @@ -58,11 +58,3 @@ } return () -> Files.move(path, externalChunkFile, StandardCopyOption.REPLACE_EXISTING); - } - - private void writeHeader() throws IOException { -- this.header.position(0); -+ ((java.nio.Buffer) this.header).position(0); // CraftBukkit - decompile error - this.file.write(this.header, 0L); - } - diff --git a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch index 56133882a..5461001e9 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/chunk/storage/SerializableChunkData.java.patch @@ -80,8 +80,8 @@ } + // CraftBukkit start - load chunk persistent data from nbt - SPIGOT-6814: Already load PDC here to account for 1.17 to 1.18 chunk upgrading. -+ if (this.persistentDataContainer instanceof CompoundTag) { -+ chunkAccess.persistentDataContainer.putAll((CompoundTag) this.persistentDataContainer); ++ if (this.persistentDataContainer instanceof CompoundTag compoundTag) { ++ chunkAccess.persistentDataContainer.putAll(compoundTag); + } + // CraftBukkit end + @@ -107,8 +107,8 @@ } + // CraftBukkit start - read/write -+ private static Codec>> makeBiomeCodecRW(Registry iregistry) { -+ return PalettedContainer.codecRW(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS)); ++ private static Codec>> makeBiomeCodecRW(Registry biomeRegistry) { ++ return PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS)); + } + // CraftBukkit end + @@ -168,8 +168,8 @@ if (structureStart != null) { + // CraftBukkit start - load persistent data for structure start + net.minecraft.nbt.Tag persistentBase = compound.getCompound(string).get("StructureBukkitValues"); -+ if (persistentBase instanceof CompoundTag) { -+ structureStart.persistentDataContainer.putAll((CompoundTag) persistentBase); ++ if (persistentBase instanceof CompoundTag compoundTag) { ++ structureStart.persistentDataContainer.putAll(compoundTag); + } + // CraftBukkit end map.put(structure, structureStart); diff --git a/paper-server/patches/sources/net/minecraft/world/level/entity/EntityAccess.java.patch b/paper-server/patches/sources/net/minecraft/world/level/entity/EntityAccess.java.patch index 6c5e3be1c..fb266ce5b 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/entity/EntityAccess.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/entity/EntityAccess.java.patch @@ -5,8 +5,8 @@ void setRemoved(Entity.RemovalReason removalReason); + // CraftBukkit start - add Bukkit remove cause -+ default void setRemoved(Entity.RemovalReason entity_removalreason, org.bukkit.event.entity.EntityRemoveEvent.Cause cause) { -+ this.setRemoved(entity_removalreason); ++ default void setRemoved(Entity.RemovalReason removalReason, org.bukkit.event.entity.EntityRemoveEvent.Cause eventCause) { ++ this.setRemoved(removalReason); + } + // CraftBukkit end + diff --git a/paper-server/patches/sources/net/minecraft/world/level/entity/PersistentEntitySectionManager.java.patch b/paper-server/patches/sources/net/minecraft/world/level/entity/PersistentEntitySectionManager.java.patch index 384e9f3c5..18b27de6e 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/entity/PersistentEntitySectionManager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/entity/PersistentEntitySectionManager.java.patch @@ -5,8 +5,8 @@ } + // CraftBukkit start - add method to get all entities in chunk -+ public List getEntities(ChunkPos chunkCoordIntPair) { -+ return this.sectionStorage.getExistingSectionsInChunk(chunkCoordIntPair.toLong()).flatMap(EntitySection::getEntities).map(entity -> (Entity) entity).collect(Collectors.toList()); ++ public List getEntities(ChunkPos chunkPos) { ++ return this.sectionStorage.getExistingSectionsInChunk(chunkPos.toLong()).flatMap(EntitySection::getEntities).map(entity -> (Entity) entity).collect(Collectors.toList()); + } + + public boolean isPending(long pair) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java.patch index c986ea09a..636705b99 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java.patch @@ -1,10 +1,5 @@ --- a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java +++ b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -@@ -1,3 +_,4 @@ -+// keep - package net.minecraft.world.level.levelgen; - - import com.google.common.annotations.VisibleForTesting; @@ -218,7 +_,7 @@ @Override public void buildSurface(WorldGenRegion level, StructureManager structureManager, RandomState random, ChunkAccess chunk) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructurePiece.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructurePiece.java.patch index 9baa2a5f6..aa1428c00 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructurePiece.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/StructurePiece.java.patch @@ -26,32 +26,32 @@ } + // CraftBukkit start -+ protected boolean placeCraftBlockEntity(ServerLevelAccessor serverLevelAccessor, BlockPos pos, org.bukkit.craftbukkit.block.CraftBlockEntityState craftBlockEntityState, int flags) { -+ if (serverLevelAccessor instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess) { ++ protected boolean placeCraftBlockEntity(ServerLevelAccessor levelAccessor, BlockPos pos, org.bukkit.craftbukkit.block.CraftBlockEntityState craftBlockEntityState, int flags) { ++ if (levelAccessor instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess) { + return transformerAccess.setCraftBlock(pos, craftBlockEntityState, flags); + } -+ boolean result = serverLevelAccessor.setBlock(pos, craftBlockEntityState.getHandle(), flags); -+ BlockEntity tileEntity = serverLevelAccessor.getBlockEntity(pos); -+ if (tileEntity != null) { -+ tileEntity.loadWithComponents(craftBlockEntityState.getSnapshotNBT(), serverLevelAccessor.registryAccess()); ++ boolean result = levelAccessor.setBlock(pos, craftBlockEntityState.getHandle(), flags); ++ BlockEntity blockEntity = levelAccessor.getBlockEntity(pos); ++ if (blockEntity != null) { ++ blockEntity.loadWithComponents(craftBlockEntityState.getSnapshotNBT(), levelAccessor.registryAccess()); + } + return result; + } + -+ protected void placeCraftSpawner(ServerLevelAccessor worldAccess, BlockPos position, org.bukkit.entity.EntityType entityType, int i) { ++ protected void placeCraftSpawner(ServerLevelAccessor levelAccessor, BlockPos pos, org.bukkit.entity.EntityType entityType, int flags) { + // This method is used in structures that are generated by code and place spawners as they set the entity after the block was placed making it impossible for plugins to access that information -+ org.bukkit.craftbukkit.block.CraftCreatureSpawner spawner = (org.bukkit.craftbukkit.block.CraftCreatureSpawner) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(worldAccess, position, Blocks.SPAWNER.defaultBlockState(), null); ++ org.bukkit.craftbukkit.block.CraftCreatureSpawner spawner = (org.bukkit.craftbukkit.block.CraftCreatureSpawner) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(levelAccessor, pos, Blocks.SPAWNER.defaultBlockState(), null); + spawner.setSpawnedType(entityType); -+ this.placeCraftBlockEntity(worldAccess, position, spawner, i); ++ this.placeCraftBlockEntity(levelAccessor, pos, spawner, flags); + } + -+ protected void setCraftLootTable(ServerLevelAccessor worldAccess, BlockPos position, RandomSource randomSource, ResourceKey loottableKey) { ++ protected void setCraftLootTable(ServerLevelAccessor levelAccessor, BlockPos pos, RandomSource randomSource, ResourceKey lootTable) { + // This method is used in structures that use data markers to a loot table to loot containers as otherwise plugins won't have access to that information. -+ net.minecraft.world.level.block.entity.BlockEntity tileEntity = worldAccess.getBlockEntity(position); -+ if (tileEntity instanceof net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity tileEntityLootable) { -+ tileEntityLootable.setLootTable(loottableKey, randomSource.nextLong()); -+ if (worldAccess instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess) { -+ transformerAccess.setCraftBlock(position, (org.bukkit.craftbukkit.block.CraftBlockState) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(worldAccess, position, tileEntity.getBlockState(), tileEntityLootable.saveWithFullMetadata(worldAccess.registryAccess())), 3); ++ net.minecraft.world.level.block.entity.BlockEntity blockEntity = levelAccessor.getBlockEntity(pos); ++ if (blockEntity instanceof net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity lootContainerBlockEntity) { ++ lootContainerBlockEntity.setLootTable(lootTable, randomSource.nextLong()); ++ if (levelAccessor instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess) { ++ transformerAccess.setCraftBlock(pos, (org.bukkit.craftbukkit.block.CraftBlockState) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(levelAccessor, pos, blockEntity.getBlockState(), lootContainerBlockEntity.saveWithFullMetadata(levelAccessor.registryAccess())), 3); + } + } + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch b/paper-server/patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch index 9457eaacf..d6fbecf87 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/material/FlowingFluid.java.patch @@ -58,7 +58,7 @@ } } -+ protected void beforeDestroyingBlock(LevelAccessor world, BlockPos pos, BlockState state, BlockPos source) { beforeDestroyingBlock(world, pos, state); } // Paper - Add BlockBreakBlockEvent ++ protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state, BlockPos source) { beforeDestroyingBlock(level, pos, state); } // Paper - Add BlockBreakBlockEvent protected abstract void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state); protected int getSlopeDistance(LevelReader level, BlockPos pos, int depth, Direction direction, BlockState state, FlowingFluid.SpreadContext spreadContext) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch b/paper-server/patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch index 59826b744..32fa03281 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/material/WaterFluid.java.patch @@ -7,9 +7,9 @@ - + // Paper start - Add BlockBreakBlockEvent + @Override -+ protected void beforeDestroyingBlock(LevelAccessor world, BlockPos pos, BlockState state, BlockPos source) { -+ BlockEntity tileentity = state.hasBlockEntity() ? world.getBlockEntity(pos) : null; -+ Block.dropResources(state, world, pos, tileentity, source); ++ protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state, BlockPos source) { ++ BlockEntity tileentity = state.hasBlockEntity() ? level.getBlockEntity(pos) : null; ++ Block.dropResources(state, level, pos, tileentity, source); + } + // Paper end - Add BlockBreakBlockEvent @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/storage/PlayerDataStorage.java.patch b/paper-server/patches/sources/net/minecraft/world/level/storage/PlayerDataStorage.java.patch index e452ec4ba..a462f3265 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/storage/PlayerDataStorage.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/storage/PlayerDataStorage.java.patch @@ -82,12 +82,12 @@ - Optional optional = this.load(player, ".dat"); + // CraftBukkit start + return this.load(player.getName().getString(), player.getStringUUID()).map((tag) -> { -+ if (player instanceof ServerPlayer) { -+ CraftPlayer player1 = (CraftPlayer) player.getBukkitEntity(); ++ if (player instanceof ServerPlayer serverPlayer) { ++ CraftPlayer craftPlayer = serverPlayer.getBukkitEntity(); + // Only update first played if it is older than the one we have + long modified = new File(this.playerDir, player.getStringUUID() + ".dat").lastModified(); -+ if (modified < player1.getFirstPlayed()) { -+ player1.setFirstPlayed(modified); ++ if (modified < craftPlayer.getFirstPlayed()) { ++ craftPlayer.setFirstPlayed(modified); + } + } + diff --git a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java new file mode 100644 index 000000000..d3c216f44 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java @@ -0,0 +1,15 @@ +package io.papermc.paper; + +import org.bukkit.craftbukkit.damage.CraftDamageEffect; +import org.bukkit.damage.DamageEffect; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class PaperServerInternalAPIBridge implements InternalAPIBridge { + public static final PaperServerInternalAPIBridge INSTANCE = new PaperServerInternalAPIBridge(); + + @Override + public DamageEffect getDamageEffect(final String key) { + return CraftDamageEffect.getById(key); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java b/paper-server/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java index 2c5702a42..7a47f0fda 100644 --- a/paper-server/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java +++ b/paper-server/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java @@ -11,7 +11,6 @@ import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; @@ -37,6 +36,7 @@ import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.DataComponentValue; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.ShadowColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextDecoration; @@ -79,6 +79,8 @@ public final class AdventureCodecs { public static final Codec COMPONENT_CODEC = recursive("adventure Component", AdventureCodecs::createCodec); public static final StreamCodec STREAM_COMPONENT_CODEC = ByteBufCodecs.fromCodecWithRegistriesTrusted(COMPONENT_CODEC); + static final Codec SHADOW_COLOR_CODEC = ExtraCodecs.ARGB_COLOR_CODEC.xmap(ShadowColor::shadowColor, ShadowColor::value); + static final Codec TEXT_COLOR_CODEC = Codec.STRING.comapFlatMap(s -> { if (s.startsWith("#")) { @Nullable TextColor value = TextColor.fromHexString(s); @@ -220,6 +222,7 @@ public final class AdventureCodecs { public static final MapCodec