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:
+ *
+ * - {@link #assetId(Key)}
+ * - {@link #translationKey(String)}
+ *
+ */
+ @ApiStatus.Experimental
+ @ApiStatus.NonExtendable
+ interface Builder extends BannerPatternRegistryEntry, RegistryBuilder {
+
+ /**
+ * Sets the asset id of the pattern type, which is the location of the sprite to use.
+ *
+ * @param assetId the asset id.
+ * @return this builder instance.
+ * @see BannerPatternRegistryEntry#assetId()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder assetId(Key assetId);
+
+ /**
+ * Sets the translation key for displaying the pattern inside the banner's tooltip.
+ *
+ * @param translationKey the translation key.
+ * @return this builder instance.
+ * @see BannerPatternRegistryEntry#translationKey()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ Builder translationKey(String translationKey);
+
+ }
+
+}
diff --git a/paper-api/src/main/java/io/papermc/paper/registry/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 super B> value);
+ default void register(final TypedKey key, final Consumer super B> 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.List extends ne
* they have seen it before because this method was called.
* Note this method does not make the player invulnerable, which is normally expected when viewing credits.
*
- * @see #hasSeenWinScreen()
- * @see #setHasSeenWinScreen(boolean)
+ * @see #hasSeenWinScreen()
+ * @see #setHasSeenWinScreen(boolean)
* @see https://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 extends Registry> 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 extends Registry> 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 super ResourceLocation, ? extends @Nullable M> existingValueGetter;
+ private @Nullable B builder;
+
+ public PaperRegistryBuilderFactory(final Conversions conversions, final PaperRegistryBuilder.Filler builderFiller, final Function super ResourceLocation, ? extends @Nullable M> 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 super B> 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 super B> 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()