diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24cccf5f903ef..ce9d2caae66fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ 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_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name + 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 runs-on: ubuntu-latest strategy: matrix: 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 0000000000000..4a76c3491baf1 --- /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/registry/RegistryBuilderFactory.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryBuilderFactory.java new file mode 100644 index 0000000000000..ce2447d3d6468 --- /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 e5319bdb9f753..ea795de9558f1 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 81bee52241960..c8f363a240362 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 0000000000000..8ca672a5bb5c8 --- /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: + *

+ */ + @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/event/RegistryEvents.java b/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java index 40deffbd09305..cfda2a7e2ecd8 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,13 @@ 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.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.enchantments.Enchantment; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; @@ -23,6 +25,7 @@ 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); 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 744f455b14cdc..fed7ad660f962 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/Registry.java b/paper-api/src/main/java/org/bukkit/Registry.java index d55c33ca14257..50eea296715be 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 @@ 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.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 @@ * * @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 Iterator iterator() { * 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 Iterator iterator() { * * @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 Iterator iterator() { * @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 Iterator iterator() { * 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 - - // Paper start - potion effect type registry + Registry GAME_EVENT = registryFor(RegistryKey.GAME_EVENT); /** - * Potion effect types. + * Data component types. * - * @see org.bukkit.potion.PotionEffectType + * @see DataComponentType */ + Registry DATA_COMPONENT_TYPE = registryFor(RegistryKey.DATA_COMPONENT_TYPE); // Paper + + // + /** + * @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 + /** + * @apiNote use {@link #SOUND_EVENT} + * @hidden + */ + @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 does not exist + * @return item or null if it does not exist */ - @Nullable - T get(@NotNull NamespacedKey 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 net.kyori.adventure.key.@NotNull Key key) { + 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 Stream stream() { * @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 Stream stream() { * * @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 Stream stream() { * * @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 Stream stream() { * @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 Stream stream() { * * @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 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 + 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 - * @see #hasTag(io.papermc.paper.registry.tag.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"); - } + 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,77 +527,82 @@ default boolean hasTag(final io.papermc.paper.registry.tag.@NotNull TagKey ke * @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); + public Iterator iterator() { + return this.map.values().iterator(); + } + + @ApiStatus.Internal + @Deprecated(since = "1.20.6", forRemoval = true) + public Class getType() { + return this.type; + } + } - Preconditions.checkArgument(object != null, "No %s registry entry found for key %s.", type, key); + @ApiStatus.Internal + abstract class NotARegistry implements Registry { - return object; + @Override + public Stream stream() { + return StreamSupport.stream(this.spliterator(), false); } - @NotNull @Override - public Stream stream() { - return StreamSupport.stream(spliterator(), false); + public NamespacedKey getKey(final A value) { + return value.getKey(); } - @NotNull @Override - public Iterator iterator() { - return map.values().iterator(); + public boolean hasTag(final TagKey key) { + throw new UnsupportedOperationException("This is not a real registry and therefore cannot support tags"); } - @ApiStatus.Internal - @Deprecated(since = "1.20.6", forRemoval = true) - public Class getType() { - return this.type; + @Override + public Tag getTag(final TagKey key) { + throw new UnsupportedOperationException("This is not a real registry and therefore cannot support tags"); } - // Paper start - improve Registry @Override - public @NotNull NamespacedKey getKey(final @NotNull T value) { - return value.getKey(); + public Collection> getTags() { + throw new UnsupportedOperationException("This is not a real registry and therefore cannot support tags"); } - // Paper end - improve Registry } } diff --git a/paper-api/src/main/java/org/bukkit/Tag.java b/paper-api/src/main/java/org/bukkit/Tag.java index 961a36e03df96..193858a281558 100644 --- a/paper-api/src/main/java/org/bukkit/Tag.java +++ b/paper-api/src/main/java/org/bukkit/Tag.java @@ -1049,10 +1049,17 @@ public interface Tag extends Keyed { * Vanilla item tag representing all items which repair wolf armor. */ Tag ITEMS_REPAIRS_WOLF_ARMOR = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("repairs_wolf_armor"), Material.class); + /** + * Vanilla item tag representing all stone based materials for crafting. + */ + 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} */ - Tag ITEMS_FURNACE_MATERIALS = Bukkit.getTag(REGISTRY_ITEMS, NamespacedKey.minecraft("furnace_materials"), Material.class); + @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 d0de7ce3c3a73..56fa266e4b5bd 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.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; @@ -198,13 +201,81 @@ default com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { */ @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]); + } + + /** + * 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); - default org.bukkit.entity.Entity deserializeEntity(byte[] data, World world) { + /** + * 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/entity/Entity.java b/paper-api/src/main/java/org/bukkit/entity/Entity.java index 19272cff8d6d0..ddf7829eee5e3 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Entity.java +++ b/paper-api/src/main/java/org/bukkit/entity/Entity.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Set; import java.util.UUID; +import io.papermc.paper.entity.LookAnchor; import org.bukkit.Chunk; // Paper import org.bukkit.EntityEffect; import org.bukkit.Location; @@ -13,6 +14,7 @@ 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; @@ -150,6 +152,26 @@ default boolean teleport(@NotNull Location location, @NotNull io.papermc.paper.e * @return true if the teleport was successful */ boolean teleport(@NotNull Location location, @NotNull TeleportCause cause, @NotNull io.papermc.paper.entity.TeleportFlag @NotNull... teleportFlags); + + /** + * Causes the entity to look towards the given position. + * + * @param x x coordinate + * @param y y coordinate + * @param z z coordinate + * @param entityAnchor What part of the entity should face the given position + */ + void lookAt(double x, double y, double z, @NotNull LookAnchor entityAnchor); + + /** + * Causes the entity to look towards the given position. + * + * @param position Position to look at in the player's current world + * @param entityAnchor What part of the entity should face the given position + */ + default void lookAt(@NotNull io.papermc.paper.math.Position position, @NotNull LookAnchor entityAnchor) { + this.lookAt(position.x(), position.y(), position.z(), entityAnchor); + } // Paper end - Teleport API /** @@ -1051,11 +1073,12 @@ default Chunk getChunk() { *

* 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); } /** @@ -1065,11 +1088,12 @@ public default boolean spawnAt(@NotNull Location location) { *

* 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/Player.java b/paper-api/src/main/java/org/bukkit/entity/Player.java index fac4aec289e07..be9bea56aad7b 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Player.java +++ b/paper-api/src/main/java/org/bukkit/entity/Player.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import io.papermc.paper.entity.LookAnchor; import org.bukkit.BanEntry; import org.bukkit.DyeColor; import org.bukkit.Effect; @@ -1309,8 +1310,8 @@ void sendSignChange(@NotNull Location loc, @Nullable java.util.Listhttps://minecraft.wiki/wiki/End_Poem#Technical_details */ public void showWinScreen(); @@ -3392,7 +3393,7 @@ default boolean hasResourcePack() { * @param simulationDistance the player's new simulation distance */ public void setSimulationDistance(int simulationDistance); - + /** * Gets the no-ticking view distance for this player. *

@@ -3730,26 +3731,6 @@ public int getPing() { */ void setRotation(float yaw, float pitch); - /** - * Causes the player to look towards the given position. - * - * @param x x coordinate - * @param y y coordinate - * @param z z coordinate - * @param playerAnchor What part of the player should face the given position - */ - void lookAt(double x, double y, double z, @NotNull io.papermc.paper.entity.LookAnchor playerAnchor); - - /** - * Causes the player to look towards the given position. - * - * @param position Position to look at in the player's current world - * @param playerAnchor What part of the player should face the given position - */ - default void lookAt(@NotNull io.papermc.paper.math.Position position, @NotNull io.papermc.paper.entity.LookAnchor playerAnchor) { - this.lookAt(position.x(), position.y(), position.z(), playerAnchor); - } - /** * Causes the player to look towards the given entity. * @@ -3757,7 +3738,7 @@ default void lookAt(@NotNull io.papermc.paper.math.Position position, @NotNull i * @param playerAnchor What part of the player should face the entity * @param entityAnchor What part of the entity the player should face */ - void lookAt(@NotNull org.bukkit.entity.Entity entity, @NotNull io.papermc.paper.entity.LookAnchor playerAnchor, @NotNull io.papermc.paper.entity.LookAnchor entityAnchor); + void lookAt(@NotNull org.bukkit.entity.Entity entity, @NotNull LookAnchor playerAnchor, @NotNull LookAnchor entityAnchor); // Paper end - Teleport API // Paper start 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 5e52f4dc26e4c..759aaa9351027 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 EquipmentSlot getHand() { @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/ItemStack.java b/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java index 8c9654cd19af8..ed95b77a0bcf0 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 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 net.kyori.adventure.text.event.HoverEvent 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/patches/features/0017-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0017-Moonrise-optimisation-patches.patch index 337302aaa2725..93faf375633ed 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() 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 1059713e9f559..4bfb8560283e7 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 400d714abee48..2a92380a2fa74 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/server/level/ServerEntity.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerEntity.java.patch index 4413d91d0fa1a..7932d7570d7bc 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 c32e052b23495..662414567d15c 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 @@ -919,7 +919,7 @@ + ((Container) menu).stopOpen(this); + } else if (menu instanceof net.minecraft.world.level.block.ChestBlock.DoubleInventory doubleInventory) { + // SPIGOT-5355 - double chests too :( -+ doubleInventory.inventorylargechest.stopOpen(this); ++ doubleInventory.container.stopOpen(this); + // Paper start - Fix InventoryOpenEvent cancellation + } else if (!this.enderChestInventory.isActiveChest(null)) { + this.enderChestInventory.stopOpen(this); 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 e21dc4c54b1ad..af6cdd217d81c 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/world/entity/Entity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch index f1de578de1fae..780a51d4089d7 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/level/block/ChestBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/ChestBlock.java.patch index 55671a0957918..5ec5cd486d8fc 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/ChestBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/ChestBlock.java.patch @@ -1,39 +1,53 @@ --- a/net/minecraft/world/level/block/ChestBlock.java +++ b/net/minecraft/world/level/block/ChestBlock.java -@@ -120,6 +_,38 @@ +@@ -85,7 +_,7 @@ + @Override + public Optional acceptDouble(final ChestBlockEntity first, final ChestBlockEntity second) { + final Container container = new CompoundContainer(first, second); +- return Optional.of(new MenuProvider() { ++ return Optional.of(DoubleInventory.wrap(new MenuProvider() { // CraftBukkit - wrap for identification + @Nullable + @Override + public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) { +@@ -106,7 +_,7 @@ + return (Component)(second.hasCustomName() ? second.getDisplayName() : Component.translatable("container.chestDouble")); + } + } +- }); ++ }, (CompoundContainer) container)); // CraftBukkit - wrap for identification + } + + @Override +@@ -120,6 +_,34 @@ } }; + // CraftBukkit start + public static class DoubleInventory implements MenuProvider { + -+ private final ChestBlockEntity tileentitychest; -+ private final ChestBlockEntity tileentitychest1; -+ public final CompoundContainer inventorylargechest; ++ private final MenuProvider delegate; ++ public final CompoundContainer container; // expose to api + -+ public DoubleInventory(ChestBlockEntity tileentitychest, ChestBlockEntity tileentitychest1, CompoundContainer inventorylargechest) { -+ this.tileentitychest = tileentitychest; -+ this.tileentitychest1 = tileentitychest1; -+ this.inventorylargechest = inventorylargechest; ++ private DoubleInventory(MenuProvider delegate, CompoundContainer container) { ++ this.delegate = delegate; ++ this.container = container; ++ } ++ ++ public static DoubleInventory wrap(MenuProvider delegate, CompoundContainer container) { ++ return new DoubleInventory(delegate, container); + } + + @Nullable + @Override + public AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) { -+ if (this.tileentitychest.canOpen(player) && this.tileentitychest1.canOpen(player)) { -+ this.tileentitychest.unpackLootTable(playerInventory.player); -+ this.tileentitychest1.unpackLootTable(playerInventory.player); -+ return ChestMenu.sixRows(syncId, playerInventory, this.inventorylargechest); -+ } else { -+ return null; -+ } ++ return this.delegate.createMenu(syncId, playerInventory, player); + } + + @Override + public Component getDisplayName() { -+ return (Component) (this.tileentitychest.hasCustomName() ? this.tileentitychest.getDisplayName() : (this.tileentitychest1.hasCustomName() ? this.tileentitychest1.getDisplayName() : Component.translatable("container.chestDouble"))); ++ return this.delegate.getDisplayName(); + } -+ }; ++ } + // CraftBukkit end + @Override 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 661176a03de87..225f53b419b7e 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 ++ } ++ // 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; + } -+ // CraftBukkit end ++ // 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/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java b/paper-server/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java index f671b74e4179f..bbd29bcca94a8 100644 --- a/paper-server/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java +++ b/paper-server/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java @@ -48,7 +48,7 @@ public List tabComplete(final CommandSender sender, final String subComm if (args.length == 1) { return CommandUtil.getListMatchingLast(sender, args, "help", "list"); } else if (args.length == 2) { - return CommandUtil.getListMatchingLast(sender, args, BuiltInRegistries.ENTITY_TYPE.keySet().stream().map(ResourceLocation::toString).sorted().toArray(String[]::new)); + return CommandUtil.getListMatchingLast(sender, args, BuiltInRegistries.ENTITY_TYPE.keySet()); } return Collections.emptyList(); } diff --git a/paper-server/src/main/java/io/papermc/paper/configuration/RemovedConfigurations.java b/paper-server/src/main/java/io/papermc/paper/configuration/RemovedConfigurations.java index f9a4bb664409a..fad321217045c 100644 --- a/paper-server/src/main/java/io/papermc/paper/configuration/RemovedConfigurations.java +++ b/paper-server/src/main/java/io/papermc/paper/configuration/RemovedConfigurations.java @@ -53,7 +53,8 @@ interface RemovedConfigurations { path("entities", "spawning", "despawn-ranges", "hard"), path("fixes", "fix-curing-zombie-villager-discount-exploit"), path("entities", "mob-effects", "undead-immune-to-certain-effects"), - path("entities", "entities-target-with-follow-range") + path("entities", "entities-target-with-follow-range"), + path("environment", "disable-teleportation-suffocation-check") }; // spawn.keep-spawn-loaded and spawn.keep-spawn-loaded-range are no longer used, but kept // in the world default config for compatibility with old worlds being migrated to use the gamerule diff --git a/paper-server/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/paper-server/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java index b1c917d65076a..f5ac1b0290661 100644 --- a/paper-server/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java +++ b/paper-server/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java @@ -445,7 +445,6 @@ public class TreasureMaps extends ConfigurationPart { public int portalSearchRadius = 128; public int portalCreateRadius = 16; public boolean portalSearchVanillaDimensionScaling = true; - public boolean disableTeleportationSuffocationCheck = false; public IntOr.Disabled netherCeilingVoidDamageHeight = IntOr.Disabled.DISABLED; public int maxFluidTicks = 65536; public int maxBlockTicks = 65536; diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java index a41356e136c87..c79981e50e3ed 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java @@ -4,6 +4,7 @@ import io.papermc.paper.adventure.PaperAdventure; import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.datacomponent.PaperDataComponentType; +import io.papermc.paper.registry.data.PaperBannerPatternRegistryEntry; import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry; import io.papermc.paper.registry.data.PaperGameEventRegistryEntry; import io.papermc.paper.registry.data.PaperPaintingVariantRegistryEntry; @@ -106,7 +107,7 @@ public final class PaperRegistries { start(Registries.WOLF_VARIANT, RegistryKey.WOLF_VARIANT).craft(Wolf.Variant.class, CraftWolf.CraftVariant::new).build().delayed(), start(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT).craft(Enchantment.class, CraftEnchantment::new).serializationUpdater(FieldRename.ENCHANTMENT_RENAME).writable(PaperEnchantmentRegistryEntry.PaperBuilder::new).delayed(), start(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG).craft(JukeboxSong.class, CraftJukeboxSong::new).build().delayed(), - start(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN).craft(PatternType.class, CraftPatternType::new).build().delayed(), + start(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN).craft(PatternType.class, CraftPatternType::new).writable(PaperBannerPatternRegistryEntry.PaperBuilder::new).delayed(), start(Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT).craft(Art.class, CraftArt::new).writable(PaperPaintingVariantRegistryEntry.PaperBuilder::new).delayed(), start(Registries.INSTRUMENT, RegistryKey.INSTRUMENT).craft(MusicInstrument.class, CraftMusicInstrument::new).build().delayed(), diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryBuilderFactory.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryBuilderFactory.java new file mode 100644 index 0000000000000..e83a6336bbd13 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistryBuilderFactory.java @@ -0,0 +1,51 @@ +package io.papermc.paper.registry; + +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.registry.data.util.Conversions; +import java.util.function.Function; +import net.minecraft.resources.ResourceLocation; +import org.bukkit.Keyed; +import org.jspecify.annotations.Nullable; + +public class PaperRegistryBuilderFactory> implements RegistryBuilderFactory { // TODO remove Keyed + + private final Conversions conversions; + private final PaperRegistryBuilder.Filler builderFiller; + private final Function existingValueGetter; + private @Nullable B builder; + + public PaperRegistryBuilderFactory(final Conversions conversions, final PaperRegistryBuilder.Filler builderFiller, final Function existingValueGetter) { + this.conversions = conversions; + this.builderFiller = builderFiller; + this.existingValueGetter = existingValueGetter; + } + + private void validate() { + if (this.builder != null) { + throw new IllegalStateException("Already created a builder"); + } + } + + public B requireBuilder() { + if (this.builder == null) { + throw new IllegalStateException("Builder not created yet"); + } + return this.builder; + } + + @Override + public B empty() { + this.validate(); + return this.builder = this.builderFiller.create(this.conversions); + } + + @Override + public B copyFrom(final TypedKey key) { + this.validate(); + final M existing = this.existingValueGetter.apply(PaperAdventure.asVanilla(key)); + if (existing == null) { + throw new IllegalArgumentException("Key " + key + " doesn't exist"); + } + return this.builder = this.builderFiller.fill(this.conversions, existing); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperSimpleRegistry.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperSimpleRegistry.java index cc39bc68d2905..7c3fdfb46efb2 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/PaperSimpleRegistry.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperSimpleRegistry.java @@ -3,6 +3,7 @@ import io.papermc.paper.registry.set.NamedRegistryKeySetImpl; import io.papermc.paper.registry.tag.Tag; import io.papermc.paper.registry.tag.TagKey; +import java.util.Collection; import java.util.function.Predicate; import net.minecraft.core.HolderSet; import net.minecraft.core.registries.BuiltInRegistries; @@ -51,4 +52,9 @@ public Tag getTag(final TagKey key) { final HolderSet.Named namedHolderSet = this.nmsRegistry.get(PaperRegistries.toNms(key)).orElseThrow(); return new NamedRegistryKeySetImpl<>(key, namedHolderSet); } + + @Override + public Collection> getTags() { + return this.nmsRegistry.getTags().>map(NamedRegistryKeySetImpl::new).toList(); + } } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java b/paper-server/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java index 6c17623e76df6..a294ec37b6f59 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java @@ -2,20 +2,15 @@ import com.mojang.serialization.Lifecycle; import io.papermc.paper.registry.data.util.Conversions; -import io.papermc.paper.registry.entry.RegistryEntry; import io.papermc.paper.registry.entry.RegistryEntryMeta; -import io.papermc.paper.registry.entry.RegistryTypeMapper; import io.papermc.paper.registry.event.WritableRegistry; import java.util.Optional; -import java.util.function.BiFunction; import java.util.function.Consumer; import net.minecraft.core.MappedRegistry; import net.minecraft.core.RegistrationInfo; import net.minecraft.resources.ResourceKey; import org.bukkit.Keyed; -import org.bukkit.NamespacedKey; import org.bukkit.craftbukkit.CraftRegistry; -import org.bukkit.craftbukkit.util.ApiVersion; public class WritableCraftRegistry> extends CraftRegistry { @@ -33,16 +28,16 @@ public WritableCraftRegistry( this.meta = meta; } - public void register(final TypedKey key, final Consumer value, final Conversions conversions) { + public void register(final TypedKey key, final Consumer> value, final Conversions conversions) { final ResourceKey resourceKey = PaperRegistries.toNms(key); this.registry.validateWrite(resourceKey); - final B builder = this.newBuilder(conversions); - value.accept(builder); + final PaperRegistryBuilderFactory builderFactory = new PaperRegistryBuilderFactory<>(conversions, this.meta.builderFiller(), this.registry.temporaryUnfrozenMap::get); + value.accept(builderFactory); PaperRegistryListenerManager.INSTANCE.registerWithListeners( this.registry, this.meta, resourceKey, - builder, + builderFactory.requireBuilder(), FROM_PLUGIN, conversions ); @@ -52,10 +47,6 @@ public WritableRegistry createApiWritableRegistry(final Conversions conver return new ApiWritableRegistry(conversions); } - protected B newBuilder(final Conversions conversions) { - return this.meta.builderFiller().create(conversions); - } - public class ApiWritableRegistry implements WritableRegistry { private final Conversions conversions; @@ -65,7 +56,7 @@ public ApiWritableRegistry(final Conversions conversions) { } @Override - public void register(final TypedKey key, final Consumer value) { + public void registerWith(final TypedKey key, final Consumer> value) { WritableCraftRegistry.this.register(key, value, this.conversions); } } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperBannerPatternRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperBannerPatternRegistryEntry.java new file mode 100644 index 0000000000000..70d226a14a8d4 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperBannerPatternRegistryEntry.java @@ -0,0 +1,65 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.registry.PaperRegistryBuilder; +import io.papermc.paper.registry.data.util.Conversions; +import net.kyori.adventure.key.Key; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.entity.BannerPattern; +import org.bukkit.block.banner.PatternType; +import org.jspecify.annotations.Nullable; + +import static io.papermc.paper.registry.data.util.Checks.*; + +public class PaperBannerPatternRegistryEntry implements BannerPatternRegistryEntry { + + protected @Nullable ResourceLocation assetId; + protected @Nullable String translationKey; + + public PaperBannerPatternRegistryEntry( + final Conversions ignoredConversions, + final @Nullable BannerPattern internal + ) { + if (internal == null) return; + + this.assetId = internal.assetId(); + this.translationKey = internal.translationKey(); + } + + @Override + public Key assetId() { + return PaperAdventure.asAdventure(asConfigured(this.assetId, "assetId")); + } + + @Override + public String translationKey() { + return asConfigured(this.translationKey, "translationKey"); + } + + public static final class PaperBuilder extends PaperBannerPatternRegistryEntry implements Builder, PaperRegistryBuilder { + + public PaperBuilder(final Conversions conversions, final @Nullable BannerPattern internal) { + super(conversions, internal); + } + + @Override + public Builder assetId(final Key assetId) { + this.assetId = PaperAdventure.asVanilla(asArgument(assetId, "assetId")); + return this; + } + + @Override + public Builder translationKey(final String translationKey) { + this.translationKey = asArgument(translationKey, "translationKey"); + return this; + } + + @Override + public BannerPattern build() { + return new BannerPattern( + asConfigured(this.assetId, "assetId"), + this.translationKey() + ); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java b/paper-server/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java index fdc475f2b112b..527fbbbbebccd 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java @@ -2,6 +2,7 @@ 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.function.Supplier; import java.util.stream.Stream; @@ -38,11 +39,6 @@ public Registry delegate() { return this.delegate().get(key); } - @Override - public T getOrThrow(final NamespacedKey key) { - return this.delegate().getOrThrow(key); - } - @Override public Iterator iterator() { return this.delegate().iterator(); @@ -67,4 +63,9 @@ public boolean hasTag(final TagKey key) { public @NonNull Tag getTag(final TagKey key) { return this.delegate().getTag(key); } + + @Override + public Collection> getTags() { + return this.delegate().getTags(); + } } diff --git a/paper-server/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java b/paper-server/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java index 7b15640c2f10c..79499fa9023f1 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java @@ -26,6 +26,10 @@ public record NamedRegistryKeySetImpl( // TODO remove Keyed HolderSet.Named namedSet ) implements Tag, org.bukkit.Tag { + public NamedRegistryKeySetImpl(final HolderSet.Named namedSet) { + this(PaperRegistries.fromNms(namedSet.key()), namedSet); + } + @Override public @Unmodifiable Collection> values() { final ImmutableList.Builder> builder = ImmutableList.builder(); diff --git a/paper-server/src/main/java/io/papermc/paper/util/OldEnumHolderable.java b/paper-server/src/main/java/io/papermc/paper/util/OldEnumHolderable.java index 1e70146847fe3..0c4ddd9ea9b53 100644 --- a/paper-server/src/main/java/io/papermc/paper/util/OldEnumHolderable.java +++ b/paper-server/src/main/java/io/papermc/paper/util/OldEnumHolderable.java @@ -9,7 +9,7 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -@SuppressWarnings("removal") +@SuppressWarnings({"removal", "DeprecatedIsStillUsed"}) @Deprecated @NullMarked public abstract class OldEnumHolderable, M> implements Holderable, OldEnum, Keyed { @@ -43,7 +43,7 @@ public Holder getHolder() { @Override @Deprecated - public int compareTo(A other) { + public int compareTo(final A other) { this.checkIsReference(); return this.ordinal - other.ordinal(); } @@ -83,6 +83,10 @@ public int hashCode() { @Override public String toString() { + if (this.name != null) { + // TODO remove in next feature release or 1.22 + return this.name; + } return this.implToString(); } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java index 946beee6588a4..1f25d054325c5 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java @@ -2,8 +2,11 @@ import com.google.common.base.Preconditions; import io.papermc.paper.registry.entry.RegistryEntryMeta; +import io.papermc.paper.registry.set.NamedRegistryKeySetImpl; +import io.papermc.paper.registry.tag.Tag; import io.papermc.paper.util.Holderable; import io.papermc.paper.util.MCUtil; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -272,7 +275,7 @@ public NamespacedKey getKey(final B value) { if (value instanceof Holderable holderable) { return holderable.getKeyOrNull(); } - return Registry.super.getKey(value); + return value.getKey(); } // Paper end - improve Registry @@ -287,6 +290,11 @@ public io.papermc.paper.registry.tag.Tag getTag(final io.papermc.paper.regist final net.minecraft.core.HolderSet.Named namedHolderSet = this.minecraftRegistry.get(io.papermc.paper.registry.PaperRegistries.toNms(key)).orElseThrow(); return new io.papermc.paper.registry.set.NamedRegistryKeySetImpl<>(key, namedHolderSet); } + + @Override + public Collection> getTags() { + return this.minecraftRegistry.getTags().>map(NamedRegistryKeySetImpl::new).toList(); + } // Paper end - RegistrySet API final class InvalidHolderOwner implements HolderOwner { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 91b78711ec06f..86388f04b180b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Set; import java.util.UUID; +import io.papermc.paper.entity.LookAnchor; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; @@ -45,6 +46,7 @@ import org.bukkit.entity.Player; import org.bukkit.entity.Pose; import org.bukkit.entity.SpawnCategory; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityRemoveEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; @@ -299,6 +301,25 @@ public boolean teleport(org.bukkit.entity.Entity destination, TeleportCause caus return this.teleport(destination.getLocation(), cause); } + @Override + public void lookAt(double x, double y, double z, LookAnchor entityAnchor) { + this.getHandle().lookAt(toNmsAnchor(entityAnchor), new net.minecraft.world.phys.Vec3(x, y, z)); + } + + public static net.minecraft.commands.arguments.EntityAnchorArgument.Anchor toNmsAnchor(LookAnchor nmsAnchor) { + return switch (nmsAnchor) { + case EYES -> net.minecraft.commands.arguments.EntityAnchorArgument.Anchor.EYES; + case FEET -> net.minecraft.commands.arguments.EntityAnchorArgument.Anchor.FEET; + }; + } + + public static LookAnchor toApiAnchor(net.minecraft.commands.arguments.EntityAnchorArgument.Anchor playerAnchor) { + return switch (playerAnchor) { + case EYES -> LookAnchor.EYES; + case FEET -> LookAnchor.FEET; + }; + } + @Override public List getNearbyEntities(double x, double y, double z) { Preconditions.checkState(!this.entity.generation, "Cannot get nearby entities during world generation"); @@ -935,7 +956,7 @@ public boolean isInWorld() { @Override public String getAsString() { CompoundTag tag = new CompoundTag(); - if (!this.getHandle().saveAsPassenger(tag, false)) { + if (!this.getHandle().saveAsPassenger(tag, false, false, false)) { return null; } @@ -968,7 +989,7 @@ public org.bukkit.entity.Entity copy(Location location) { private Entity copy(net.minecraft.world.level.Level level) { CompoundTag compoundTag = new CompoundTag(); - this.getHandle().saveAsPassenger(compoundTag, false); + this.getHandle().saveAsPassenger(compoundTag, false, true, true); return net.minecraft.world.entity.EntityType.loadEntityRecursive(compoundTag, level, EntitySpawnReason.LOAD, java.util.function.Function.identity()); } @@ -1204,17 +1225,19 @@ public Set getTrackedPlayers() { } // Paper end - tracked players API - // Paper start - raw entity serialization API @Override - public boolean spawnAt(Location location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { + public boolean spawnAt(Location location, CreatureSpawnEvent.SpawnReason reason) { Preconditions.checkNotNull(location, "location cannot be null"); Preconditions.checkNotNull(reason, "reason cannot be null"); this.entity.setLevel(((CraftWorld) location.getWorld()).getHandle()); this.entity.setPos(location.getX(), location.getY(), location.getZ()); this.entity.setRot(location.getYaw(), location.getPitch()); - return !this.entity.valid && this.entity.level().addFreshEntity(this.entity, reason); + final boolean spawned = !this.entity.valid && this.entity.level().addFreshEntity(this.entity, reason); + if (!spawned) return false; // Do not attempt to spawn rest if root was not spawned in + // Like net.minecraft.world.level.ServerLevelAccessor.addFreshEntityWithPassengers(net.minecraft.world.entity.Entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason) + this.entity.getIndirectPassengers().forEach(e -> e.level().addFreshEntity(e, reason)); + return true; } - // Paper end - raw entity serialization API // Paper start - entity powdered snow API @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntitySnapshot.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntitySnapshot.java index 6642bdc117d54..cb8d7fe3a07bb 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntitySnapshot.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntitySnapshot.java @@ -66,7 +66,7 @@ public CompoundTag getData() { public static CraftEntitySnapshot create(CraftEntity entity) { CompoundTag tag = new CompoundTag(); - if (!entity.getHandle().saveAsPassenger(tag, false)) { + if (!entity.getHandle().saveAsPassenger(tag, false, false, false)) { return null; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 18100c2c7ca82..ceb672bfaeb29 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -7,6 +7,7 @@ import com.mojang.datafixers.util.Pair; import io.netty.buffer.Unpooled; import io.papermc.paper.FeatureHooks; +import io.papermc.paper.entity.LookAnchor; import it.unimi.dsi.fastutil.shorts.ShortArraySet; import it.unimi.dsi.fastutil.shorts.ShortSet; import java.io.ByteArrayOutputStream; @@ -1377,29 +1378,10 @@ public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cau } @Override - public void lookAt(@NotNull org.bukkit.entity.Entity entity, @NotNull io.papermc.paper.entity.LookAnchor playerAnchor, @NotNull io.papermc.paper.entity.LookAnchor entityAnchor) { + public void lookAt(@NotNull org.bukkit.entity.Entity entity, @NotNull LookAnchor playerAnchor, @NotNull LookAnchor entityAnchor) { this.getHandle().lookAt(toNmsAnchor(playerAnchor), ((CraftEntity) entity).getHandle(), toNmsAnchor(entityAnchor)); } - @Override - public void lookAt(double x, double y, double z, @NotNull io.papermc.paper.entity.LookAnchor playerAnchor) { - this.getHandle().lookAt(toNmsAnchor(playerAnchor), new net.minecraft.world.phys.Vec3(x, y, z)); - } - - public static net.minecraft.commands.arguments.EntityAnchorArgument.Anchor toNmsAnchor(io.papermc.paper.entity.LookAnchor nmsAnchor) { - return switch (nmsAnchor) { - case EYES -> net.minecraft.commands.arguments.EntityAnchorArgument.Anchor.EYES; - case FEET -> net.minecraft.commands.arguments.EntityAnchorArgument.Anchor.FEET; - }; - } - - public static io.papermc.paper.entity.LookAnchor toApiAnchor(net.minecraft.commands.arguments.EntityAnchorArgument.Anchor playerAnchor) { - return switch (playerAnchor) { - case EYES -> io.papermc.paper.entity.LookAnchor.EYES; - case FEET -> io.papermc.paper.entity.LookAnchor.FEET; - }; - } - public static net.minecraft.world.entity.Relative deltaRelativeToNMS(io.papermc.paper.entity.TeleportFlag.Relative apiFlag) { return switch (apiFlag) { case VELOCITY_X -> net.minecraft.world.entity.Relative.DELTA_X; @@ -1896,7 +1878,7 @@ public int applyMending(int amount) { handle.serverLevel(), itemstack, amount ); int i = Math.min(possibleDurabilityFromXp, itemstack.getDamageValue()); - final int consumedExperience = i * amount / possibleDurabilityFromXp; // Paper - taken from ExperienceOrb#repairPlayerItems + final int consumedExperience = i > 0 ? i * amount / possibleDurabilityFromXp : possibleDurabilityFromXp; // Paper - taken from ExperienceOrb#repairPlayerItems + prevent division by 0 org.bukkit.event.player.PlayerItemMendEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemMendEvent(handle, orb, itemstack, stackEntry.get().inSlot(), i, consumedExperience); i = event.getRepairAmount(); orb.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java index c3e2c9e2c3cbe..67f9ec03c4479 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java @@ -16,10 +16,10 @@ public class CraftInventoryDoubleChest extends CraftInventory implements DoubleC private final CraftInventory right; public CraftInventoryDoubleChest(ChestBlock.DoubleInventory block) { - super(block.inventorylargechest); + super(block.container); this.tile = block; - this.left = new CraftInventory(block.inventorylargechest.container1); - this.right = new CraftInventory(block.inventorylargechest.container2); + this.left = new CraftInventory(block.container.container1); + this.right = new CraftInventory(block.container.container2); } public CraftInventoryDoubleChest(CompoundContainer largeChest) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java index 78975412da0f0..3799973696eab 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -5,6 +5,8 @@ import java.util.Collections; import java.util.Map; import java.util.Optional; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; import net.minecraft.advancements.critereon.ItemPredicate; import net.minecraft.advancements.critereon.MinMaxBounds; import net.minecraft.core.Holder; @@ -25,6 +27,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.material.MaterialData; +import org.jetbrains.annotations.NotNull; @DelegateDeserialization(ItemStack.class) public final class CraftItemStack extends ItemStack { @@ -467,6 +470,11 @@ void skullCallback(final net.minecraft.world.item.component.ResolvableProfile re return true; } + @Override + public @NotNull Component effectiveName() { + return this.handle == null ? Component.empty() : PaperAdventure.asAdventure(this.handle.getStyledHoverName()); + } + @Override public boolean isSimilar(ItemStack stack) { if (stack == null) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftEnchantmentView.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftEnchantmentView.java index abe709ab9002b..37a02aa2530f7 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftEnchantmentView.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftEnchantmentView.java @@ -47,7 +47,7 @@ public EnchantmentOffer[] getOffers() { @Override public void setOffers(@NotNull final EnchantmentOffer[] offers) { - Preconditions.checkArgument(offers.length != 3, "There must be 3 offers given"); + Preconditions.checkArgument(offers.length == 3, "There must be 3 offers given"); IdMap> registry = CraftRegistry.getMinecraftRegistry().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap(); for (int i = 0; i < offers.length; i++) { final EnchantmentOffer offer = offers[i]; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java index fe29c08270854..23cbe3b6f4c7d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java @@ -282,6 +282,7 @@ public static PotionType valueOf_PotionType(String name) { .change("PONDER", "PONDER_GOAT_HORN") .change("SING", "SING_GOAT_HORN") .change("SEEK", "SEEK_GOAT_HORN") + .change("FEEL", "FEEL_GOAT_HORN") .change("ADMIRE", "ADMIRE_GOAT_HORN") .change("CALL", "CALL_GOAT_HORN") .change("YEARN", "YEARN_GOAT_HORN") diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java index b6665e18758db..5f6d67b0ee8a0 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.util; +import ca.spottedleaf.moonrise.common.PlatformHooks; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.collect.Multimap; @@ -13,24 +14,34 @@ import com.mojang.serialization.JsonOps; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Stream; +import io.papermc.paper.entity.EntitySerializationFlag; import net.minecraft.SharedConstants; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.item.ItemParser; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; import net.minecraft.util.datafix.DataFixers; import net.minecraft.util.datafix.fixes.References; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.alchemy.Potion; import net.minecraft.world.level.block.Block; @@ -43,6 +54,7 @@ import org.bukkit.NamespacedKey; import org.bukkit.Registry; import org.bukkit.UnsafeValues; +import org.bukkit.World; import org.bukkit.advancement.Advancement; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeModifier; @@ -51,10 +63,12 @@ // import org.bukkit.craftbukkit.CraftFeatureFlag; // Paper import org.bukkit.craftbukkit.CraftRegistry; import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBiome; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.damage.CraftDamageEffect; import org.bukkit.craftbukkit.damage.CraftDamageSourceBuilder; +import org.bukkit.craftbukkit.entity.CraftEntity; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.craftbukkit.legacy.CraftLegacy; import org.bukkit.craftbukkit.legacy.FieldRename; @@ -513,7 +527,7 @@ public byte[] serializeItem(ItemStack item) { Preconditions.checkNotNull(item, "null cannot be serialized"); Preconditions.checkArgument(item.getType() != Material.AIR, "air cannot be serialized"); - return serializeNbtToBytes((net.minecraft.nbt.CompoundTag) (item instanceof CraftItemStack ? ((CraftItemStack) item).handle : CraftItemStack.asNMSCopy(item)).save(MinecraftServer.getServer().registryAccess())); + return serializeNbtToBytes((CompoundTag) (item instanceof CraftItemStack ? ((CraftItemStack) item).handle : CraftItemStack.asNMSCopy(item)).save(MinecraftServer.getServer().registryAccess())); } @Override @@ -521,9 +535,9 @@ public ItemStack deserializeItem(byte[] data) { Preconditions.checkNotNull(data, "null cannot be deserialized"); Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing"); - net.minecraft.nbt.CompoundTag compound = deserializeNbtFromBytes(data); + CompoundTag compound = deserializeNbtFromBytes(data); final int dataVersion = compound.getInt("DataVersion"); - compound = ca.spottedleaf.moonrise.common.PlatformHooks.get().convertNBT(References.ITEM_STACK, MinecraftServer.getServer().fixerUpper, compound, dataVersion, this.getDataVersion()); // Paper - possibly use dataconverter + compound = PlatformHooks.get().convertNBT(References.ITEM_STACK, MinecraftServer.getServer().fixerUpper, compound, dataVersion, this.getDataVersion()); // Paper - possibly use dataconverter return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.parse(MinecraftServer.getServer().registryAccess(), compound).orElseThrow()); } @@ -558,32 +572,98 @@ public ItemStack deserializeItemFromJson(com.google.gson.JsonObject data) throws } @Override - public byte[] serializeEntity(org.bukkit.entity.Entity entity) { + public byte[] serializeEntity(org.bukkit.entity.Entity entity, EntitySerializationFlag... serializationFlags) { Preconditions.checkNotNull(entity, "null cannot be serialized"); - Preconditions.checkArgument(entity instanceof org.bukkit.craftbukkit.entity.CraftEntity, "only CraftEntities can be serialized"); + Preconditions.checkArgument(entity instanceof CraftEntity, "Only CraftEntities can be serialized"); + + Set flags = Set.of(serializationFlags); + final boolean serializePassangers = flags.contains(EntitySerializationFlag.PASSENGERS); + final boolean forceSerialization = flags.contains(EntitySerializationFlag.FORCE); + final boolean allowPlayerSerialization = flags.contains(EntitySerializationFlag.PLAYER); + final boolean allowMiscSerialization = flags.contains(EntitySerializationFlag.MISC); + final boolean includeNonSaveable = allowPlayerSerialization || allowMiscSerialization; + + net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) entity).getHandle(); + (serializePassangers ? nmsEntity.getSelfAndPassengers() : Stream.of(nmsEntity)).forEach(e -> { + // Ensure force flag is not needed + Preconditions.checkArgument( + (e.getBukkitEntity().isValid() && e.getBukkitEntity().isPersistent()) || forceSerialization, + "Cannot serialize invalid or non-persistent entity %s(%s) without the FORCE flag", + e.getType().toShortString(), + e.getStringUUID() + ); + + if (e instanceof Player) { + // Ensure player flag is not needed + Preconditions.checkArgument( + allowPlayerSerialization, + "Cannot serialize player(%s) without the PLAYER flag", + e.getStringUUID() + ); + } else { + // Ensure player flag is not needed + Preconditions.checkArgument( + nmsEntity.getType().canSerialize() || allowMiscSerialization, + "Cannot serialize misc non-saveable entity %s(%s) without the MISC flag", + e.getType().toShortString(), + e.getStringUUID() + ); + } + }); - net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag(); - ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().serializeEntity(compound); + CompoundTag compound = new CompoundTag(); + if (serializePassangers) { + if (!nmsEntity.saveAsPassenger(compound, true, includeNonSaveable, forceSerialization)) { + throw new IllegalArgumentException("Couldn't serialize entity"); + } + } else { + List pass = new ArrayList<>(nmsEntity.getPassengers()); + nmsEntity.passengers = com.google.common.collect.ImmutableList.of(); + boolean serialized = nmsEntity.saveAsPassenger(compound, true, includeNonSaveable, forceSerialization); + nmsEntity.passengers = com.google.common.collect.ImmutableList.copyOf(pass); + if (!serialized) { + throw new IllegalArgumentException("Couldn't serialize entity"); + } + } return serializeNbtToBytes(compound); } @Override - public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world, boolean preserveUUID) { + public org.bukkit.entity.Entity deserializeEntity(byte[] data, World world, boolean preserveUUID, boolean preservePassengers) { Preconditions.checkNotNull(data, "null cannot be deserialized"); - Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing"); + Preconditions.checkArgument(data.length > 0, "Cannot deserialize empty data"); - net.minecraft.nbt.CompoundTag compound = deserializeNbtFromBytes(data); + CompoundTag compound = deserializeNbtFromBytes(data); int dataVersion = compound.getInt("DataVersion"); - compound = ca.spottedleaf.moonrise.common.PlatformHooks.get().convertNBT(References.ENTITY, MinecraftServer.getServer().fixerUpper, compound, dataVersion, this.getDataVersion()); // Paper - possibly use dataconverter + compound = PlatformHooks.get().convertNBT(References.ENTITY, MinecraftServer.getServer().fixerUpper, compound, dataVersion, this.getDataVersion()); // Paper - possibly use dataconverter + if (!preservePassengers) { + compound.remove("Passengers"); + } + net.minecraft.world.entity.Entity nmsEntity = deserializeEntity(compound, ((CraftWorld) world).getHandle(), preserveUUID); + return nmsEntity.getBukkitEntity(); + } + + private net.minecraft.world.entity.Entity deserializeEntity(CompoundTag compound, ServerLevel world, boolean preserveUUID) { if (!preserveUUID) { - // Generate a new UUID so we don't have to worry about deserializing the same entity twice + // Generate a new UUID, so we don't have to worry about deserializing the same entity twice compound.remove("UUID"); } - return net.minecraft.world.entity.EntityType.create(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle(), net.minecraft.world.entity.EntitySpawnReason.LOAD) - .orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?")).getBukkitEntity(); + net.minecraft.world.entity.Entity nmsEntity = net.minecraft.world.entity.EntityType.create(compound, world, net.minecraft.world.entity.EntitySpawnReason.LOAD) + .orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?")); + if (compound.contains("Passengers", Tag.TAG_LIST)) { + ListTag passengersCompound = compound.getList("Passengers", Tag.TAG_COMPOUND); + for (Tag tag : passengersCompound) { + if (!(tag instanceof CompoundTag serializedPassenger)) { + continue; + } + net.minecraft.world.entity.Entity passengerEntity = deserializeEntity(serializedPassenger, world, preserveUUID); + passengerEntity.startRiding(nmsEntity, true); + } + } + return nmsEntity; } - private byte[] serializeNbtToBytes(net.minecraft.nbt.CompoundTag compound) { + private byte[] serializeNbtToBytes(CompoundTag compound) { compound.putInt("DataVersion", getDataVersion()); java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream(); try { @@ -597,8 +677,8 @@ private byte[] serializeNbtToBytes(net.minecraft.nbt.CompoundTag compound) { return outputStream.toByteArray(); } - private net.minecraft.nbt.CompoundTag deserializeNbtFromBytes(byte[] data) { - net.minecraft.nbt.CompoundTag compound; + private CompoundTag deserializeNbtFromBytes(byte[] data) { + CompoundTag compound; try { compound = net.minecraft.nbt.NbtIo.readCompressed( new java.io.ByteArrayInputStream(data), net.minecraft.nbt.NbtAccounter.unlimitedHeap()