Merge remote-tracking branch 'upstream/main' into update

This commit is contained in:
2024-12-31 17:12:20 +01:00
172 changed files with 2994 additions and 1313 deletions

View File

@@ -0,0 +1,15 @@
package io.papermc.paper;
import org.bukkit.craftbukkit.damage.CraftDamageEffect;
import org.bukkit.damage.DamageEffect;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class PaperServerInternalAPIBridge implements InternalAPIBridge {
public static final PaperServerInternalAPIBridge INSTANCE = new PaperServerInternalAPIBridge();
@Override
public DamageEffect getDamageEffect(final String key) {
return CraftDamageEffect.getById(key);
}
}

View File

@@ -11,7 +11,6 @@ import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -37,6 +36,7 @@ import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.DataComponentValue;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.ShadowColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
@@ -79,6 +79,8 @@ public final class AdventureCodecs {
public static final Codec<Component> COMPONENT_CODEC = recursive("adventure Component", AdventureCodecs::createCodec);
public static final StreamCodec<RegistryFriendlyByteBuf, Component> STREAM_COMPONENT_CODEC = ByteBufCodecs.fromCodecWithRegistriesTrusted(COMPONENT_CODEC);
static final Codec<ShadowColor> SHADOW_COLOR_CODEC = ExtraCodecs.ARGB_COLOR_CODEC.xmap(ShadowColor::shadowColor, ShadowColor::value);
static final Codec<TextColor> TEXT_COLOR_CODEC = Codec.STRING.comapFlatMap(s -> {
if (s.startsWith("#")) {
@Nullable TextColor value = TextColor.fromHexString(s);
@@ -220,6 +222,7 @@ public final class AdventureCodecs {
public static final MapCodec<Style> STYLE_MAP_CODEC = mapCodec((instance) -> {
return instance.group(
TEXT_COLOR_CODEC.optionalFieldOf("color").forGetter(nullableGetter(Style::color)),
SHADOW_COLOR_CODEC.optionalFieldOf("shadow_color").forGetter(nullableGetter(Style::shadowColor)),
Codec.BOOL.optionalFieldOf("bold").forGetter(decorationGetter(TextDecoration.BOLD)),
Codec.BOOL.optionalFieldOf("italic").forGetter(decorationGetter(TextDecoration.ITALIC)),
Codec.BOOL.optionalFieldOf("underlined").forGetter(decorationGetter(TextDecoration.UNDERLINED)),
@@ -229,9 +232,10 @@ public final class AdventureCodecs {
HOVER_EVENT_CODEC.optionalFieldOf("hoverEvent").forGetter(nullableGetter(Style::hoverEvent)),
Codec.STRING.optionalFieldOf("insertion").forGetter(nullableGetter(Style::insertion)),
KEY_CODEC.optionalFieldOf("font").forGetter(nullableGetter(Style::font))
).apply(instance, (textColor, bold, italic, underlined, strikethrough, obfuscated, clickEvent, hoverEvent, insertion, font) -> {
).apply(instance, (textColor, shadowColor, bold, italic, underlined, strikethrough, obfuscated, clickEvent, hoverEvent, insertion, font) -> {
return Style.style(builder -> {
textColor.ifPresent(builder::color);
shadowColor.ifPresent(builder::shadowColor);
bold.ifPresent(styleBooleanConsumer(builder, TextDecoration.BOLD));
italic.ifPresent(styleBooleanConsumer(builder, TextDecoration.ITALIC));
underlined.ifPresent(styleBooleanConsumer(builder, TextDecoration.UNDERLINED));

View File

@@ -48,7 +48,7 @@ public final class EntityCommand implements PaperSubcommand {
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();
}

View File

@@ -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

View File

@@ -445,7 +445,6 @@ public class WorldConfiguration 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;

View File

@@ -2,71 +2,25 @@ package io.papermc.paper.datapack;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.event.server.ServerResourcesReloadedEvent;
import io.papermc.paper.world.flag.PaperFeatureFlagProviderImpl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.kyori.adventure.text.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.packs.repository.Pack;
import net.minecraft.server.packs.repository.PackSource;
import org.bukkit.FeatureFlag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.DefaultQualifier;
import org.jspecify.annotations.NullMarked;
@DefaultQualifier(NonNull.class)
public class PaperDatapack implements Datapack {
private static final Map<PackSource, DatapackSource> PACK_SOURCES = new ConcurrentHashMap<>();
static {
PACK_SOURCES.put(PackSource.DEFAULT, DatapackSource.DEFAULT);
PACK_SOURCES.put(PackSource.BUILT_IN, DatapackSource.BUILT_IN);
PACK_SOURCES.put(PackSource.FEATURE, DatapackSource.FEATURE);
PACK_SOURCES.put(PackSource.WORLD, DatapackSource.WORLD);
PACK_SOURCES.put(PackSource.SERVER, DatapackSource.SERVER);
}
@NullMarked
public class PaperDatapack extends PaperDiscoveredDatapack implements Datapack {
private final Pack pack;
private final boolean enabled;
PaperDatapack(final Pack pack, final boolean enabled) {
super(pack);
this.pack = pack;
this.enabled = enabled;
}
@Override
public String getName() {
return this.pack.getId();
}
@Override
public Component getTitle() {
return PaperAdventure.asAdventure(this.pack.getTitle());
}
@Override
public Component getDescription() {
return PaperAdventure.asAdventure(this.pack.getDescription());
}
@Override
public boolean isRequired() {
return this.pack.isRequired();
}
@Override
public Compatibility getCompatibility() {
return Datapack.Compatibility.valueOf(this.pack.getCompatibility().name());
}
@Override
public Set<FeatureFlag> getRequiredFeatures() {
return PaperFeatureFlagProviderImpl.fromNms(this.pack.getRequestedFeatures());
}
@Override
public boolean isEnabled() {
return this.enabled;
@@ -76,7 +30,7 @@ public class PaperDatapack implements Datapack {
public void setEnabled(final boolean enabled) {
final MinecraftServer server = MinecraftServer.getServer();
final List<Pack> enabledPacks = new ArrayList<>(server.getPackRepository().getSelectedPacks());
final @Nullable Pack packToChange = server.getPackRepository().getPack(this.getName());
final Pack packToChange = server.getPackRepository().getPack(this.getName());
if (packToChange == null) {
throw new IllegalStateException("Cannot toggle state of pack that doesn't exist: " + this.getName());
}
@@ -91,11 +45,6 @@ public class PaperDatapack implements Datapack {
server.reloadResources(enabledPacks.stream().map(Pack::getId).toList(), ServerResourcesReloadedEvent.Cause.PLUGIN);
}
@Override
public DatapackSource getSource() {
return PACK_SOURCES.computeIfAbsent(this.pack.location().source(), source -> new DatapackSourceImpl(source.toString()));
}
@Override
public Component computeDisplayName() {
return PaperAdventure.asAdventure(this.pack.getChatLink(this.enabled));

View File

@@ -0,0 +1,167 @@
package io.papermc.paper.datapack;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.mojang.logging.LogUtils;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
import io.papermc.paper.plugin.configuration.PluginMeta;
import io.papermc.paper.plugin.lifecycle.event.registrar.PaperRegistrar;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.function.Consumer;
import net.kyori.adventure.text.Component;
import net.minecraft.server.packs.PackLocationInfo;
import net.minecraft.server.packs.PackSelectionConfig;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.VanillaPackResourcesBuilder;
import net.minecraft.server.packs.repository.FolderRepositorySource;
import net.minecraft.server.packs.repository.Pack;
import net.minecraft.server.packs.repository.PackDetector;
import net.minecraft.world.level.validation.ContentValidationException;
import net.minecraft.world.level.validation.DirectoryValidator;
import net.minecraft.world.level.validation.ForbiddenSymlinkInfo;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
@NullMarked
public class PaperDatapackRegistrar implements PaperRegistrar<BootstrapContext>, DatapackRegistrar {
private static final Logger LOGGER = LogUtils.getClassLogger();
private final PackDetector<Pack.ResourcesSupplier> detector;
public final Map<String, Pack> discoveredPacks;
private @Nullable BootstrapContext owner;
public PaperDatapackRegistrar(final DirectoryValidator symlinkValidator, final Map<String, Pack> discoveredPacks) {
this.detector = new FolderRepositorySource.FolderPackDetector(symlinkValidator);
this.discoveredPacks = discoveredPacks;
}
@Override
public void setCurrentContext(final @Nullable BootstrapContext owner) {
this.owner = owner;
}
@Override
public boolean hasPackDiscovered(final String name) {
return this.discoveredPacks.containsKey(name);
}
@Override
public DiscoveredDatapack getDiscoveredPack(final String name) {
if (!this.hasPackDiscovered(name)) {
throw new NoSuchElementException("No pack with id " + name + " was discovered");
}
return new PaperDiscoveredDatapack(this.discoveredPacks.get(name));
}
@Override
public boolean removeDiscoveredPack(final String name) {
return this.discoveredPacks.remove(name) != null;
}
@Override
public @Unmodifiable Map<String, DiscoveredDatapack> getDiscoveredPacks() {
final ImmutableMap.Builder<String, DiscoveredDatapack> builder = ImmutableMap.builderWithExpectedSize(this.discoveredPacks.size());
for (final Map.Entry<String, Pack> entry : this.discoveredPacks.entrySet()) {
builder.put(entry.getKey(), new PaperDiscoveredDatapack(entry.getValue()));
}
return builder.buildOrThrow();
}
@Override
public @Nullable DiscoveredDatapack discoverPack(final URI uri, final String id, final Consumer<Configurer> configurer) throws IOException {
Preconditions.checkState(this.owner != null, "Discovering packs is not supported outside of lifecycle events");
return this.discoverPack(this.owner.getPluginMeta(), uri, id, configurer);
}
@Override
public @Nullable DiscoveredDatapack discoverPack(final Path path, final String id, final Consumer<Configurer> configurer) throws IOException {
Preconditions.checkState(this.owner != null, "Discovering packs is not supported outside of lifecycle events");
return this.discoverPack(this.owner.getPluginMeta(), path, id, configurer);
}
@Override
public @Nullable DiscoveredDatapack discoverPack(final PluginMeta pluginMeta, final URI uri, final String id, final Consumer<Configurer> configurer) throws IOException {
return this.discoverPack(pluginMeta, VanillaPackResourcesBuilder.safeGetPath(uri), id, configurer);
}
@Override
public @Nullable DiscoveredDatapack discoverPack(final PluginMeta pluginMeta, final Path path, final String id, final Consumer<Configurer> configurer) throws IOException {
Preconditions.checkState(this.owner != null, "Discovering packs is not supported outside of lifecycle events");
final List<ForbiddenSymlinkInfo> badLinks = new ArrayList<>();
final Pack.ResourcesSupplier resourcesSupplier = this.detector.detectPackResources(path, badLinks);
if (!badLinks.isEmpty()) {
LOGGER.warn("Ignoring potential pack entry: {}", ContentValidationException.getMessage(path, badLinks));
return null;
} else if (resourcesSupplier != null) {
final String packId = pluginMeta.getName() + "/" + id;
final ConfigurerImpl configurerImpl = new ConfigurerImpl(Component.text(packId));
configurer.accept(configurerImpl);
final PackLocationInfo locInfo = new PackLocationInfo(packId,
PaperAdventure.asVanilla(configurerImpl.title),
PluginPackSource.INSTANCE,
Optional.empty()
);
final Pack pack = Pack.readMetaAndCreate(locInfo,
resourcesSupplier,
PackType.SERVER_DATA,
new PackSelectionConfig(
configurerImpl.autoEnableOnServerStart,
configurerImpl.position,
configurerImpl.fixedPosition
));
if (pack != null) {
this.discoveredPacks.put(packId, pack);
return new PaperDiscoveredDatapack(pack);
}
return null;
} else {
LOGGER.info("Found non-pack entry '{}', ignoring", path);
return null;
}
}
static final class ConfigurerImpl implements Configurer {
private Component title;
private boolean autoEnableOnServerStart = false;
private boolean fixedPosition = false;
private Pack.Position position = Pack.Position.TOP;
ConfigurerImpl(final Component title) {
this.title = title;
}
@Override
public Configurer title(final Component title) {
this.title = title;
return this;
}
@Override
public Configurer autoEnableOnServerStart(final boolean autoEnableOnServerStart) {
this.autoEnableOnServerStart = autoEnableOnServerStart;
return this;
}
@Override
public Configurer position(final boolean fixed, final Datapack.Position position) {
this.fixedPosition = fixed;
this.position = switch (position) {
case TOP -> Pack.Position.TOP;
case BOTTOM -> Pack.Position.BOTTOM;
};
return this;
}
}
}

View File

@@ -0,0 +1,69 @@
package io.papermc.paper.datapack;
import com.google.common.collect.ImmutableMap;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.world.flag.PaperFeatureFlagProviderImpl;
import java.util.Map;
import java.util.Set;
import net.kyori.adventure.text.Component;
import net.minecraft.server.packs.repository.Pack;
import net.minecraft.server.packs.repository.PackSource;
import org.bukkit.FeatureFlag;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class PaperDiscoveredDatapack implements DiscoveredDatapack {
private static final Map<PackSource, DatapackSource> PACK_SOURCES;
static {
PACK_SOURCES = ImmutableMap.<PackSource, DatapackSource>builder()
.put(PackSource.DEFAULT, DatapackSource.DEFAULT)
.put(PackSource.BUILT_IN, DatapackSource.BUILT_IN)
.put(PackSource.FEATURE, DatapackSource.FEATURE)
.put(PackSource.WORLD, DatapackSource.WORLD)
.put(PackSource.SERVER, DatapackSource.SERVER)
.put(PluginPackSource.INSTANCE, DatapackSource.PLUGIN)
.buildOrThrow();
}
private final Pack pack;
PaperDiscoveredDatapack(final Pack pack) {
this.pack = pack;
}
@Override
public String getName() {
return this.pack.getId();
}
@Override
public Component getTitle() {
return PaperAdventure.asAdventure(this.pack.getTitle());
}
@Override
public Component getDescription() {
return PaperAdventure.asAdventure(this.pack.getDescription());
}
@Override
public boolean isRequired() {
return this.pack.isRequired();
}
@Override
public Datapack.Compatibility getCompatibility() {
return Datapack.Compatibility.valueOf(this.pack.getCompatibility().name());
}
@Override
public Set<FeatureFlag> getRequiredFeatures() {
return PaperFeatureFlagProviderImpl.fromNms(this.pack.getRequestedFeatures());
}
@Override
public DatapackSource getSource() {
return PACK_SOURCES.computeIfAbsent(this.pack.location().source(), source -> new DatapackSourceImpl(source.toString()));
}
}

View File

@@ -0,0 +1,25 @@
package io.papermc.paper.datapack;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.server.packs.repository.PackSource;
import org.jspecify.annotations.NullMarked;
@NullMarked
final class PluginPackSource implements PackSource {
static final PackSource INSTANCE = new PluginPackSource();
private PluginPackSource() {
}
@Override
public Component decorate(final Component packDisplayName) {
return Component.translatable("pack.nameAndSource", packDisplayName, "plugin").withStyle(ChatFormatting.GRAY);
}
@Override
public boolean shouldAddAutomatically() {
return true;
}
}

View File

@@ -6,10 +6,9 @@ import io.papermc.paper.plugin.storage.ProviderStorage;
import io.papermc.paper.plugin.storage.ServerPluginProviderStorage;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
import org.jetbrains.annotations.ApiStatus;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.ApiStatus;
/**
* Used by the server to register/load plugin bootstrappers and plugins.

View File

@@ -4,10 +4,13 @@ import com.google.common.base.Preconditions;
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.PaperDamageTypeRegistryEntry;
import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry;
import io.papermc.paper.registry.data.PaperGameEventRegistryEntry;
import io.papermc.paper.registry.data.PaperPaintingVariantRegistryEntry;
import io.papermc.paper.registry.entry.RegistryEntry;
import io.papermc.paper.registry.entry.RegistryEntryMeta;
import io.papermc.paper.registry.tag.TagKey;
import java.util.Collections;
import java.util.IdentityHashMap;
@@ -102,11 +105,11 @@ public final class PaperRegistries {
start(Registries.STRUCTURE, RegistryKey.STRUCTURE).craft(Structure.class, CraftStructure::new).build().delayed(),
start(Registries.TRIM_MATERIAL, RegistryKey.TRIM_MATERIAL).craft(TrimMaterial.class, CraftTrimMaterial::new).build().delayed(),
start(Registries.TRIM_PATTERN, RegistryKey.TRIM_PATTERN).craft(TrimPattern.class, CraftTrimPattern::new).build().delayed(),
start(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE).craft(DamageType.class, CraftDamageType::new).build().delayed(),
start(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE).craft(DamageType.class, CraftDamageType::new).writable(PaperDamageTypeRegistryEntry.PaperBuilder::new).delayed(),
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(),
@@ -136,6 +139,18 @@ public final class PaperRegistries {
return (RegistryEntry<M, T>) BY_REGISTRY_KEY.get(registryKey);
}
@SuppressWarnings("unchecked")
public static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntryMeta.Buildable<M, T, B> getBuildableMeta(final ResourceKey<? extends Registry<M>> resourceKey) {
final RegistryEntry<M, T> entry = getEntry(resourceKey);
if (entry == null) {
throw new IllegalArgumentException("No registry entry for " + resourceKey);
}
if (!(entry.meta() instanceof final RegistryEntryMeta.Buildable<M, T, ?> buildableMeta)) {
throw new IllegalArgumentException("Registry entry for " + resourceKey + " is not buildable");
}
return (RegistryEntryMeta.Buildable<M, T, B>) buildableMeta;
}
@SuppressWarnings("unchecked")
public static <M, T> RegistryKey<T> registryFromNms(final ResourceKey<? extends Registry<M>> registryResourceKey) {
return (RegistryKey<T>) Objects.requireNonNull(BY_RESOURCE_KEY.get(registryResourceKey), registryResourceKey + " doesn't have an api RegistryKey").apiKey();

View File

@@ -13,6 +13,7 @@ import java.util.stream.Collectors;
import net.minecraft.resources.ResourceKey;
import org.bukkit.Keyed;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.jetbrains.annotations.VisibleForTesting;
import org.jspecify.annotations.Nullable;
@@ -72,7 +73,7 @@ public class PaperRegistryAccess implements RegistryAccess {
if (PaperRegistries.getEntry(key) == null) {
throw new NoSuchElementException(key + " is not a valid registry key");
}
final @Nullable RegistryHolder<T> registryHolder = (RegistryHolder<T>) this.registries.get(key);
final RegistryHolder<T> registryHolder = (RegistryHolder<T>) this.registries.get(key);
if (registryHolder == null) {
throw new IllegalArgumentException(key + " points to a registry that is not available yet");
}
@@ -104,13 +105,22 @@ public class PaperRegistryAccess implements RegistryAccess {
this.registerRegistry(resourceKey, registry, false);
}
public <M> void lockReferenceHolders(final ResourceKey<? extends net.minecraft.core.Registry<M>> resourceKey) {
final RegistryEntry<M, Keyed> entry = PaperRegistries.getEntry(resourceKey);
if (entry == null || !(entry.meta() instanceof final RegistryEntryMeta.ServerSide<M, Keyed> serverSide) || !serverSide.registryTypeMapper().supportsDirectHolders()) {
return;
}
final CraftRegistry<?, M> registry = (CraftRegistry<?, M>) this.getRegistry(entry.apiKey());
registry.lockReferenceHolders();
}
@SuppressWarnings("unchecked") // this method should be called right after any new MappedRegistry instances are created to later be used by the server.
private <M, B extends Keyed, R extends Registry<B>> void registerRegistry(final ResourceKey<? extends net.minecraft.core.Registry<M>> resourceKey, final net.minecraft.core.Registry<M> registry, final boolean replace) {
final @Nullable RegistryEntry<M, B> entry = PaperRegistries.getEntry(resourceKey);
final RegistryEntry<M, B> entry = PaperRegistries.getEntry(resourceKey);
if (entry == null) { // skip registries that don't have API entries
return;
}
final @Nullable RegistryHolder<B> registryHolder = (RegistryHolder<B>) this.registries.get(entry.apiKey());
final RegistryHolder<B> registryHolder = (RegistryHolder<B>) this.registries.get(entry.apiKey());
if (registryHolder == null || replace) {
// if the holder doesn't exist yet, or is marked as "replaceable", put it in the map.
this.registries.put(entry.apiKey(), entry.createRegistryHolder(registry));

View File

@@ -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<M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> implements RegistryBuilderFactory<A, B> { // TODO remove Keyed
private final Conversions conversions;
private final PaperRegistryBuilder.Filler<M, A, B> builderFiller;
private final Function<? super ResourceLocation, ? extends @Nullable M> existingValueGetter;
private @Nullable B builder;
public PaperRegistryBuilderFactory(final Conversions conversions, final PaperRegistryBuilder.Filler<M, A, B> 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<A> 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);
}
}

View File

@@ -3,6 +3,7 @@ package io.papermc.paper.registry;
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 class PaperSimpleRegistry<T extends Enum<T> & Keyed, M> extends Registry.
final HolderSet.Named<M> namedHolderSet = this.nmsRegistry.get(PaperRegistries.toNms(key)).orElseThrow();
return new NamedRegistryKeySetImpl<>(key, namedHolderSet);
}
@Override
public Collection<Tag<T>> getTags() {
return this.nmsRegistry.getTags().<Tag<T>>map(NamedRegistryKeySetImpl::new).toList();
}
}

View File

@@ -2,20 +2,15 @@ package io.papermc.paper.registry;
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<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistry<T, M> {
@@ -33,16 +28,16 @@ public class WritableCraftRegistry<M, T extends Keyed, B extends PaperRegistryBu
this.meta = meta;
}
public void register(final TypedKey<T> key, final Consumer<? super B> value, final Conversions conversions) {
public void register(final TypedKey<T> key, final Consumer<RegistryBuilderFactory<T, B>> value, final Conversions conversions) {
final ResourceKey<M> resourceKey = PaperRegistries.toNms(key);
this.registry.validateWrite(resourceKey);
final B builder = this.newBuilder(conversions);
value.accept(builder);
final PaperRegistryBuilderFactory<M, T, B> 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 class WritableCraftRegistry<M, T extends Keyed, B extends PaperRegistryBu
return new ApiWritableRegistry(conversions);
}
protected B newBuilder(final Conversions conversions) {
return this.meta.builderFiller().create(conversions);
}
public class ApiWritableRegistry implements WritableRegistry<T, B> {
private final Conversions conversions;
@@ -65,7 +56,7 @@ public class WritableCraftRegistry<M, T extends Keyed, B extends PaperRegistryBu
}
@Override
public void register(final TypedKey<T> key, final Consumer<? super B> value) {
public void registerWith(final TypedKey<T> key, final Consumer<RegistryBuilderFactory<T, B>> value) {
WritableCraftRegistry.this.register(key, value, this.conversions);
}
}

View File

@@ -0,0 +1,34 @@
package io.papermc.paper.registry.data;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.PaperRegistries;
import io.papermc.paper.registry.PaperRegistryBuilder;
import io.papermc.paper.registry.PaperRegistryBuilderFactory;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.data.util.Conversions;
import io.papermc.paper.registry.entry.RegistryEntryMeta;
import java.util.function.Consumer;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import org.bukkit.Art;
import org.bukkit.Keyed;
import org.bukkit.craftbukkit.CraftRegistry;
@SuppressWarnings("BoundedWildcard")
public final class InlinedRegistryBuilderProviderImpl implements InlinedRegistryBuilderProvider {
private static <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> A create(final ResourceKey<? extends Registry<M>> registryKey, final Consumer<PaperRegistryBuilderFactory<M, A, B>> value) {
final RegistryEntryMeta.Buildable<M, A, B> buildableMeta = PaperRegistries.getBuildableMeta(registryKey);
Preconditions.checkArgument(buildableMeta.registryTypeMapper().supportsDirectHolders(), "Registry type mapper must support direct holders");
final PaperRegistryBuilderFactory<M, A, B> builderFactory = new PaperRegistryBuilderFactory<>(Conversions.global(), buildableMeta.builderFiller(), CraftRegistry.getMinecraftRegistry(buildableMeta.mcKey())::getValue);
value.accept(builderFactory);
return buildableMeta.registryTypeMapper().convertDirectHolder(Holder.direct(builderFactory.requireBuilder().build()));
}
@Override
public Art createPaintingVariant(final Consumer<RegistryBuilderFactory<Art, ? extends PaintingVariantRegistryEntry.Builder>> value) {
return create(Registries.PAINTING_VARIANT, value::accept);
}
}

View File

@@ -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<BannerPattern, PatternType> {
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()
);
}
}
}

View File

@@ -0,0 +1,112 @@
package io.papermc.paper.registry.data;
import io.papermc.paper.registry.PaperRegistryBuilder;
import io.papermc.paper.registry.data.util.Conversions;
import net.minecraft.world.damagesource.DamageEffects;
import net.minecraft.world.damagesource.DamageScaling;
import net.minecraft.world.damagesource.DamageType;
import net.minecraft.world.damagesource.DeathMessageType;
import org.bukkit.craftbukkit.damage.CraftDamageEffect;
import org.bukkit.craftbukkit.damage.CraftDamageType;
import org.bukkit.damage.DamageEffect;
import org.jspecify.annotations.Nullable;
import static io.papermc.paper.registry.data.util.Checks.asConfigured;
public class PaperDamageTypeRegistryEntry implements DamageTypeRegistryEntry {
protected @Nullable String messageId;
protected @Nullable Float exhaustion;
protected @Nullable DamageScaling damageScaling;
protected DamageEffects damageEffects = DamageEffects.HURT;
protected DeathMessageType deathMessageType = DeathMessageType.DEFAULT;
protected final Conversions conversions;
public PaperDamageTypeRegistryEntry(
final Conversions conversions,
final @Nullable DamageType internal
) {
this.conversions = conversions;
if (internal == null) return;
this.messageId = internal.msgId();
this.exhaustion = internal.exhaustion();
this.damageScaling = internal.scaling();
this.damageEffects = internal.effects();
this.deathMessageType = internal.deathMessageType();
}
@Override
public String messageId() {
return asConfigured(messageId, "messsageId");
}
@Override
public float exhaustion() {
return asConfigured(exhaustion, "exhaustion");
}
@Override
public org.bukkit.damage.DamageScaling damageScaling() {
return CraftDamageType.damageScalingToBukkit(asConfigured(this.damageScaling, "damageScaling"));
}
@Override
public DamageEffect damageEffect() {
return CraftDamageEffect.toBukkit(damageEffects);
}
@Override
public org.bukkit.damage.DeathMessageType deathMessageType() {
return CraftDamageType.deathMessageTypeToBukkit(deathMessageType);
}
public static final class PaperBuilder extends PaperDamageTypeRegistryEntry implements DamageTypeRegistryEntry.Builder, PaperRegistryBuilder<DamageType, org.bukkit.damage.DamageType> {
public PaperBuilder(final Conversions conversions, final @Nullable DamageType internal) {
super(conversions, internal);
}
@Override
public Builder messageId(final String messageId) {
this.messageId = messageId;
return this;
}
@Override
public Builder exhaustion(final float exhaustion) {
this.exhaustion = exhaustion;
return this;
}
@Override
public Builder damageScaling(final org.bukkit.damage.DamageScaling scaling) {
this.damageScaling = CraftDamageType.damageScalingToNMS(scaling);
return this;
}
@Override
public Builder damageEffect(final DamageEffect effect) {
this.damageEffects = ((CraftDamageEffect) effect).getHandle();
return this;
}
@Override
public Builder deathMessageType(final org.bukkit.damage.DeathMessageType deathMessageType) {
this.deathMessageType = CraftDamageType.deathMessageTypeToNMS(deathMessageType);
return this;
}
@Override
public DamageType build() {
return new DamageType(
asConfigured(this.messageId, "messsageId"),
asConfigured(this.damageScaling, "scaling"),
asConfigured(this.exhaustion, "exhaustion"),
this.damageEffects,
this.deathMessageType
);
}
}
}

View File

@@ -2,6 +2,7 @@ package io.papermc.paper.registry.legacy;
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 final class DelayedRegistry<T extends Keyed, R extends Registry<T>> imple
return this.delegate().get(key);
}
@Override
public T getOrThrow(final NamespacedKey key) {
return this.delegate().getOrThrow(key);
}
@Override
public Iterator<T> iterator() {
return this.delegate().iterator();
@@ -67,4 +63,9 @@ public final class DelayedRegistry<T extends Keyed, R extends Registry<T>> imple
public @NonNull Tag<T> getTag(final TagKey<T> key) {
return this.delegate().getTag(key);
}
@Override
public Collection<Tag<T>> getTags() {
return this.delegate().getTags();
}
}

View File

@@ -26,6 +26,10 @@ public record NamedRegistryKeySetImpl<T extends Keyed, M>( // TODO remove Keyed
HolderSet.Named<M> namedSet
) implements Tag<T>, org.bukkit.Tag<T> {
public NamedRegistryKeySetImpl(final HolderSet.Named<M> namedSet) {
this(PaperRegistries.fromNms(namedSet.key()), namedSet);
}
@Override
public @Unmodifiable Collection<TypedKey<T>> values() {
final ImmutableList.Builder<TypedKey<T>> builder = ImmutableList.builder();

View File

@@ -9,7 +9,7 @@ import org.bukkit.util.OldEnum;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@SuppressWarnings("removal")
@SuppressWarnings({"removal", "DeprecatedIsStillUsed"})
@Deprecated
@NullMarked
public abstract class OldEnumHolderable<A extends OldEnum<A>, M> implements Holderable<M>, OldEnum<A>, Keyed {
@@ -43,7 +43,7 @@ public abstract class OldEnumHolderable<A extends OldEnum<A>, M> implements Hold
@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 abstract class OldEnumHolderable<A extends OldEnum<A>, M> implements Hold
@Override
public String toString() {
if (this.name != null) {
// TODO remove in next feature release or 1.22
return this.name;
}
return this.implToString();
}
}

View File

@@ -41,9 +41,6 @@ public class CraftMusicInstrument extends MusicInstrument implements io.papermc.
return io.papermc.paper.util.Holderable.fromBukkitSerializationObject(string, Instrument.CODEC, Registry.INSTRUMENT); // Paper - switch to Holder
}
private final NamespacedKey key;
private final Instrument handle;
// Paper start - switch to Holder
@Override
public boolean equals(final Object o) {
@@ -63,8 +60,6 @@ public class CraftMusicInstrument extends MusicInstrument implements io.papermc.
private final Holder<Instrument> holder;
public CraftMusicInstrument(Holder<Instrument> holder) {
this.holder = holder;
this.key = holder.unwrapKey().map(io.papermc.paper.util.MCUtil::fromResourceKey).orElse(null);
this.handle = holder.value();
// Paper end - switch to Holder
}

View File

@@ -2,67 +2,30 @@ package org.bukkit.craftbukkit;
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 java.util.Collection;
import io.papermc.paper.util.MCUtil;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.stream.Stream;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderOwner;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import org.bukkit.Art;
import org.bukkit.Fluid;
import org.bukkit.GameEvent;
import org.bukkit.JukeboxSong;
import org.bukkit.Keyed;
import org.bukkit.MusicInstrument;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.Registry;
import org.bukkit.Sound;
import org.bukkit.attribute.Attribute;
import org.bukkit.block.Biome;
import org.bukkit.block.BlockType;
import org.bukkit.block.banner.PatternType;
import org.bukkit.craftbukkit.attribute.CraftAttribute;
import org.bukkit.craftbukkit.block.CraftBiome;
import org.bukkit.craftbukkit.block.CraftBlockType;
import org.bukkit.craftbukkit.block.banner.CraftPatternType;
import org.bukkit.craftbukkit.damage.CraftDamageType;
import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
import org.bukkit.craftbukkit.entity.CraftCat;
import org.bukkit.craftbukkit.entity.CraftFrog;
import org.bukkit.craftbukkit.entity.CraftVillager;
import org.bukkit.craftbukkit.entity.CraftWolf;
import org.bukkit.craftbukkit.generator.structure.CraftStructure;
import org.bukkit.craftbukkit.generator.structure.CraftStructureType;
import org.bukkit.craftbukkit.inventory.CraftItemType;
import org.bukkit.craftbukkit.inventory.CraftMenuType;
import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial;
import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern;
import org.bukkit.craftbukkit.legacy.FieldRename;
import org.bukkit.craftbukkit.map.CraftMapCursor;
import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
import org.bukkit.craftbukkit.util.ApiVersion;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.damage.DamageType;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Cat;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Frog;
import org.bukkit.entity.Villager;
import org.bukkit.entity.Wolf;
import org.bukkit.generator.structure.Structure;
import org.bukkit.generator.structure.StructureType;
import org.bukkit.inventory.ItemType;
import org.bukkit.inventory.MenuType;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
import org.bukkit.map.MapCursor;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
@@ -78,7 +41,7 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
return CraftRegistry.registry;
}
public static <E> net.minecraft.core.Registry<E> getMinecraftRegistry(ResourceKey<net.minecraft.core.Registry<E>> key) {
public static <E> net.minecraft.core.Registry<E> getMinecraftRegistry(ResourceKey<? extends net.minecraft.core.Registry<E>> key) {
return CraftRegistry.getMinecraftRegistry().lookupOrThrow(key);
}
@@ -202,7 +165,8 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
private final net.minecraft.core.Registry<M> minecraftRegistry;
private final io.papermc.paper.registry.entry.RegistryTypeMapper<M, B> minecraftToBukkit; // Paper - switch to Holder
private final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater; // Paper - rename to make it *clear* what it is *only* for
private boolean init;
private final InvalidHolderOwner invalidHolderOwner = new InvalidHolderOwner();
private boolean lockReferenceHolders;
public CraftRegistry(Class<?> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<? super NamespacedKey, M, B> minecraftToBukkit, BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater) { // Paper - relax preload class
// Paper start - switch to Holder
@@ -217,6 +181,22 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
this.minecraftRegistry = minecraftRegistry;
this.minecraftToBukkit = minecraftToBukkit;
this.serializationUpdater = serializationUpdater;
this.lockReferenceHolders = !this.minecraftToBukkit.supportsDirectHolders();
}
public void lockReferenceHolders() {
Preconditions.checkState(this.cache.isEmpty(), "Registry %s is already loaded", this.minecraftRegistry.key());
try {
Class.forName(this.bukkitClass.getName()); // this should always trigger the initialization of the class
} catch (final ClassNotFoundException e) {
throw new IllegalStateException("Failed to load class " + this.bukkitClass.getSimpleName(), e);
}
if (!this.minecraftToBukkit.supportsDirectHolders()) {
return;
}
Preconditions.checkState(!this.lockReferenceHolders, "Reference holders are already locked");
this.lockReferenceHolders = true;
}
// Paper - inline into CraftRegistry#get(Registry, NamespacedKey, ApiVersion) above
@@ -228,28 +208,19 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
return cached;
}
// Make sure that the bukkit class is loaded before creating an instance.
// This ensures that only one instance with a given key is created.
//
// Without this code (when bukkit class is not loaded):
// Registry#get -> #createBukkit -> (load class -> create default) -> put in cache
// Result: Registry#get != <bukkitClass>.<field> for possible one registry item
//
// With this code (when bukkit class is not loaded):
// Registry#get -> (load class -> create default) -> Registry#get -> get from cache
// Result: Registry#get == <bukkitClass>.<field>
if (!this.init) {
this.init = true;
try {
Class.forName(this.bukkitClass.getName());
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not load registry class " + this.bukkitClass, e);
}
return this.get(namespacedKey);
final Optional<Holder.Reference<M>> holderOptional = this.minecraftRegistry.get(CraftNamespacedKey.toMinecraft(namespacedKey));
final Holder.Reference<M> holder;
if (holderOptional.isPresent()) {
holder = holderOptional.get();
} else if (!this.lockReferenceHolders && this.minecraftToBukkit.supportsDirectHolders()) { // only works if its Holderable
// we lock the reference holders after the preload class has been initialized
// this is to support the vanilla mechanic of preventing vanilla registry entries being loaded. We need
// to create something to fill the API constant fields, so we create a dummy reference holder.
holder = Holder.Reference.createStandAlone(this.invalidHolderOwner, MCUtil.toResourceKey(this.minecraftRegistry.key(), namespacedKey));
} else {
holder = null;
}
B bukkit = this.createBukkit(namespacedKey, this.minecraftRegistry.get(CraftNamespacedKey.toMinecraft(namespacedKey)).orElse(null)); // Paper - switch to Holder
final B bukkit = this.createBukkit(namespacedKey, holder);
if (bukkit == null) {
return null;
}
@@ -304,7 +275,7 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
if (value instanceof Holderable<?> holderable) {
return holderable.getKeyOrNull();
}
return Registry.super.getKey(value);
return value.getKey();
}
// Paper end - improve Registry
@@ -319,5 +290,13 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
final net.minecraft.core.HolderSet.Named<M> 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<Tag<B>> getTags() {
return this.minecraftRegistry.getTags().<Tag<B>>map(NamedRegistryKeySetImpl::new).toList();
}
// Paper end - RegistrySet API
final class InvalidHolderOwner implements HolderOwner<M> {
}
}

View File

@@ -2182,7 +2182,8 @@ public class CraftWorld extends CraftRegionAccessor implements World {
receivers == null ? this.getHandle().players() : receivers.stream().map(player -> ((CraftPlayer) player).getHandle()).collect(java.util.stream.Collectors.toList()), // Paper - Particle API
sender != null ? ((CraftPlayer) sender).getHandle() : null, // Sender // Paper - Particle API
CraftParticle.createParticleParam(particle, data), // Particle
false, force,
force,
false,
x, y, z, // Position
count, // Count
offsetX, offsetY, offsetZ, // Random offset

View File

@@ -1,11 +1,11 @@
package org.bukkit.craftbukkit.enchantments;
import com.google.common.base.Preconditions;
import io.papermc.paper.util.Holderable;
import java.util.Locale;
import net.minecraft.Util;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.tags.EnchantmentTags;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
@@ -13,13 +13,12 @@ import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.legacy.FieldRename;
import org.bukkit.craftbukkit.util.ApiVersion;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.enchantments.EnchantmentWrapper;
import org.bukkit.inventory.ItemStack;
public class CraftEnchantment extends Enchantment implements Handleable<net.minecraft.world.item.enchantment.Enchantment> {
public class CraftEnchantment extends Enchantment implements Holderable<net.minecraft.world.item.enchantment.Enchantment> {
public static Enchantment minecraftToBukkit(net.minecraft.world.item.enchantment.Enchantment minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.ENCHANTMENT, Registry.ENCHANTMENT);
@@ -56,22 +55,20 @@ public class CraftEnchantment extends Enchantment implements Handleable<net.mine
return CraftRegistry.get(Registry.ENCHANTMENT, key, ApiVersion.CURRENT);
}
private final NamespacedKey key;
private final Holder<net.minecraft.world.item.enchantment.Enchantment> handle;
public CraftEnchantment(NamespacedKey key, net.minecraft.world.item.enchantment.Enchantment handle) {
this.key = key;
this.handle = CraftRegistry.getMinecraftRegistry(Registries.ENCHANTMENT).wrapAsHolder(handle);
public CraftEnchantment(Holder<net.minecraft.world.item.enchantment.Enchantment> holder) {
this.handle = holder;
}
@Override
public net.minecraft.world.item.enchantment.Enchantment getHandle() {
return this.handle.value();
public Holder<net.minecraft.world.item.enchantment.Enchantment> getHolder() {
return this.handle;
}
@Override
public NamespacedKey getKey() {
return this.key;
return Holderable.super.getKey();
}
@Override
@@ -251,24 +248,16 @@ public class CraftEnchantment extends Enchantment implements Handleable<net.mine
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof CraftEnchantment)) {
return false;
}
return this.getKey().equals(((Enchantment) other).getKey());
return Holderable.super.implEquals(other);
}
@Override
public int hashCode() {
return this.getKey().hashCode();
return Holderable.super.implHashCode();
}
@Override
public String toString() {
return "CraftEnchantment[" + this.getKey() + "]";
return Holderable.super.implToString();
}
}

View File

@@ -46,6 +46,7 @@ import org.bukkit.entity.EntityType;
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;
@@ -955,7 +956,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
@Override
public String getAsString() {
CompoundTag tag = new CompoundTag();
if (!this.getHandle().saveAsPassenger(tag, false)) {
if (!this.getHandle().saveAsPassenger(tag, false, false, false)) {
return null;
}
@@ -988,7 +989,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
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());
}
@@ -1224,17 +1225,19 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
}
// 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

View File

@@ -66,7 +66,7 @@ public class CraftEntitySnapshot implements EntitySnapshot {
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;
}

View File

@@ -7,6 +7,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
@@ -19,6 +20,7 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -48,13 +50,14 @@ import org.bukkit.craftbukkit.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.inventory.CraftMerchantCustom;
import org.bukkit.craftbukkit.inventory.CraftRecipe;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.craftbukkit.util.CraftLocation;
import org.bukkit.entity.Firework;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Villager;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
@@ -66,6 +69,8 @@ import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
private CraftInventoryPlayer inventory;
@@ -801,6 +806,47 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
// Paper end - Fix HumanEntity#drop not updating the client inv
}
@Override
@Nullable
public Item dropItem(final int slot, final int amount, final boolean throwRandomly, final @Nullable Consumer<Item> entityOperation) {
Preconditions.checkArgument(slot >= 0 && slot < this.inventory.getSize(), "Slot %s is not a valid inventory slot.", slot);
return internalDropItemFromInventory(this.inventory.getItem(slot), amount, throwRandomly, entityOperation);
}
@Override
@Nullable
public Item dropItem(final @NotNull EquipmentSlot slot, final int amount, final boolean throwRandomly, final @Nullable Consumer<Item> entityOperation) {
return internalDropItemFromInventory(this.inventory.getItem(slot), amount, throwRandomly, entityOperation);
}
@Nullable
private Item internalDropItemFromInventory(final ItemStack originalItemStack, final int amount, final boolean throwRandomly, final @Nullable Consumer<Item> entityOperation) {
if (originalItemStack == null || originalItemStack.isEmpty() || amount <= 0) return null;
final net.minecraft.world.item.ItemStack nmsItemStack = CraftItemStack.unwrap(originalItemStack);
final net.minecraft.world.item.ItemStack dropContent = nmsItemStack.split(amount);
// This will return the itemstack back to its original amount in case events fail
final ItemEntity droppedEntity = this.getHandle().drop(dropContent, throwRandomly, true, true, entityOperation);
return droppedEntity == null ? null : (Item) droppedEntity.getBukkitEntity();
}
@Override
@Nullable
public Item dropItem(final ItemStack itemStack, final boolean throwRandomly, final @Nullable Consumer<Item> entityOperation) {
// This method implementation differs from the previous dropItem implementations, as it does not source
// its itemstack from the players inventory. As such, we cannot reuse #internalDropItemFromInventory.
Preconditions.checkArgument(itemStack != null, "Cannot drop a null itemstack");
if (itemStack.isEmpty()) return null;
final net.minecraft.world.item.ItemStack nmsItemStack = CraftItemStack.asNMSCopy(itemStack);
// Do *not* call the event here, the item is not in the player inventory, they are not dropping it / do not need recovering logic (which would be a dupe).
final ItemEntity droppedEntity = this.getHandle().drop(nmsItemStack, throwRandomly, true, false, entityOperation);
return droppedEntity == null ? null : (Item) droppedEntity.getBukkitEntity();
}
@Override
public float getExhaustion() {
return this.getHandle().getFoodData().exhaustionLevel;

View File

@@ -16,7 +16,7 @@ public class CraftPainting extends CraftHanging implements Painting {
@Override
public Art getArt() {
return org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(org.bukkit.Registry.ART, this.getHandle().getVariant()).orElseThrow(() -> new IllegalStateException("Inlined painting variants are not supported yet in the API!")); // Paper
return CraftArt.minecraftHolderToBukkit(this.getHandle().getVariant());
}
@Override

View File

@@ -2983,7 +2983,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public <T> void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data, boolean force) {
ClientboundLevelParticlesPacket packetplayoutworldparticles = new ClientboundLevelParticlesPacket(CraftParticle.createParticleParam(particle, data), false, force, x, y, z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count); // Paper - fix x/y/z precision loss
ClientboundLevelParticlesPacket packetplayoutworldparticles = new ClientboundLevelParticlesPacket(CraftParticle.createParticleParam(particle, data), force, false, x, y, z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count); // Paper - fix x/y/z precision loss
this.getHandle().connection.send(packetplayoutworldparticles);
}

View File

@@ -291,13 +291,13 @@ public class CustomChunkGenerator extends InternalChunkGenerator {
}
@Override
public void applyBiomeDecoration(WorldGenLevel world, ChunkAccess chunk, StructureManager structureAccessor) {
public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) {
WorldgenRandom random = CustomChunkGenerator.getSeededRandom();
int x = chunk.getPos().x;
int z = chunk.getPos().z;
random.setSeed(Mth.getSeed(x, "should-decoration".hashCode(), z) ^ world.getSeed());
super.applyBiomeDecoration(world, chunk, structureAccessor, this.generator.shouldGenerateDecorations(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z));
random.setSeed(Mth.getSeed(x, "should-decoration".hashCode(), z) ^ level.getSeed());
super.applyBiomeDecoration(level, chunk, structureManager, this.generator.shouldGenerateDecorations(this.world.getWorld(), new RandomSourceWrapper.RandomWrapper(random), x, z));
}
@Override

View File

@@ -13,9 +13,9 @@ public class CraftBlockInventoryHolder implements BlockInventoryHolder {
private final Block block;
private final Inventory inventory;
public CraftBlockInventoryHolder(LevelAccessor world, BlockPos pos, Container inv) {
this.block = CraftBlock.at(world, pos);
this.inventory = new CraftInventory(inv);
public CraftBlockInventoryHolder(LevelAccessor levelAccessor, BlockPos pos, Container container) {
this.block = CraftBlock.at(levelAccessor, pos);
this.inventory = new CraftInventory(container);
}
// Paper start - Add missing InventoryHolders
public CraftBlockInventoryHolder(net.minecraft.world.inventory.ContainerLevelAccess levelAccess, Inventory inventory) {

View File

@@ -5,6 +5,8 @@ import com.google.common.collect.ImmutableMap;
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.enchantments.Enchantment;
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 @@ public final class CraftItemStack extends ItemStack {
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) {

View File

@@ -30,9 +30,6 @@ public class CraftTrimMaterial implements TrimMaterial, io.papermc.paper.util.Ho
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.TRIM_MATERIAL); // Paper - switch to Holder
}
private final NamespacedKey key;
private final net.minecraft.world.item.equipment.trim.TrimMaterial handle;
// Paper start - switch to Holder
private final Holder<net.minecraft.world.item.equipment.trim.TrimMaterial> holder;
@@ -64,8 +61,6 @@ public class CraftTrimMaterial implements TrimMaterial, io.papermc.paper.util.Ho
}
public CraftTrimMaterial(final Holder<net.minecraft.world.item.equipment.trim.TrimMaterial> holder) {
this.key = holder.unwrapKey().map(io.papermc.paper.util.MCUtil::fromResourceKey).orElse(null);
this.handle = holder.value();
this.holder = holder;
// Paper end - switch to Holder
}
@@ -84,14 +79,14 @@ public class CraftTrimMaterial implements TrimMaterial, io.papermc.paper.util.Ho
@NotNull
@Override
public String getTranslationKey() {
if (!(this.handle.description().getContents() instanceof TranslatableContents)) throw new UnsupportedOperationException("Description isn't translatable!"); // Paper
return ((TranslatableContents) this.handle.description().getContents()).getKey();
if (!(this.getHandle().description().getContents() instanceof TranslatableContents)) throw new UnsupportedOperationException("Description isn't translatable!"); // Paper
return ((TranslatableContents) this.getHandle().description().getContents()).getKey();
}
// Paper start - adventure
@Override
public net.kyori.adventure.text.Component description() {
return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.handle.description());
return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.getHandle().description());
}
// Paper end - adventure
}

View File

@@ -30,9 +30,6 @@ public class CraftTrimPattern implements TrimPattern, io.papermc.paper.util.Hold
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.TRIM_PATTERN); // Paper - switch to Holder
}
private final NamespacedKey key;
private final net.minecraft.world.item.equipment.trim.TrimPattern handle;
// Paper start - switch to Holder
private final Holder<net.minecraft.world.item.equipment.trim.TrimPattern> holder; // Paper - switch to Holder
@@ -64,8 +61,6 @@ public class CraftTrimPattern implements TrimPattern, io.papermc.paper.util.Hold
}
public CraftTrimPattern(Holder<net.minecraft.world.item.equipment.trim.TrimPattern> handle) {
this.key = handle.unwrapKey().map(io.papermc.paper.util.MCUtil::fromResourceKey).orElse(null);
this.handle = handle.value();
this.holder = handle;
// Paper end - switch to Holder
}
@@ -84,14 +79,14 @@ public class CraftTrimPattern implements TrimPattern, io.papermc.paper.util.Hold
@NotNull
@Override
public String getTranslationKey() {
if (!(this.handle.description().getContents() instanceof TranslatableContents)) throw new UnsupportedOperationException("Description isn't translatable!"); // Paper
return ((TranslatableContents) this.handle.description().getContents()).getKey();
if (!(this.getHandle().description().getContents() instanceof TranslatableContents)) throw new UnsupportedOperationException("Description isn't translatable!"); // Paper
return ((TranslatableContents) this.getHandle().description().getContents()).getKey();
}
// Paper start - adventure
@Override
public net.kyori.adventure.text.Component description() {
return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.handle.description());
return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.getHandle().description());
}
// Paper end - adventure
}

View File

@@ -47,7 +47,7 @@ public class CraftEnchantmentView extends CraftInventoryView<EnchantmentMenu, En
@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<Holder<Enchantment>> registry = CraftRegistry.getMinecraftRegistry().lookupOrThrow(Registries.ENCHANTMENT).asHolderIdMap();
for (int i = 0; i < offers.length; i++) {
final EnchantmentOffer offer = offers[i];

View File

@@ -282,6 +282,7 @@ public class FieldRename {
.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")

View File

@@ -5,6 +5,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
@@ -19,9 +20,9 @@ import org.bukkit.map.MapView;
public final class CraftMapView implements MapView {
private final Map<CraftPlayer, RenderData> renderCache = new HashMap<CraftPlayer, RenderData>();
private final List<MapRenderer> renderers = new ArrayList<MapRenderer>();
private final Map<MapRenderer, Map<CraftPlayer, CraftMapCanvas>> canvases = new HashMap<MapRenderer, Map<CraftPlayer, CraftMapCanvas>>();
private final Map<CraftPlayer, RenderData> renderCache = new WeakHashMap<>();
private final List<MapRenderer> renderers = new ArrayList<>();
private final Map<MapRenderer, Map<CraftPlayer, CraftMapCanvas>> canvases = new HashMap<>();
protected final MapItemSavedData worldMap;
public CraftMapView(MapItemSavedData worldMap) {
@@ -99,7 +100,7 @@ public final class CraftMapView implements MapView {
public void addRenderer(MapRenderer renderer) {
if (!this.renderers.contains(renderer)) {
this.renderers.add(renderer);
this.canvases.put(renderer, new HashMap<CraftPlayer, CraftMapCanvas>());
this.canvases.put(renderer, new WeakHashMap<>());
renderer.initialize(this);
}
}

View File

@@ -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.Dynamic;
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.Material;
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.block.data.BlockData;
// 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;
@@ -466,12 +480,6 @@ public final class CraftMagicNumbers implements UnsafeValues {
return new CraftPotionType(namespacedKey, potionRegistry);
}
@Override
public DamageEffect getDamageEffect(String key) {
Preconditions.checkArgument(key != null, "key cannot be null");
return CraftDamageEffect.getById(key);
}
@Override
public DamageSource.Builder createDamageSourceBuilder(DamageType damageType) {
return new CraftDamageSourceBuilder(damageType);
@@ -513,7 +521,7 @@ public final class CraftMagicNumbers implements UnsafeValues {
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 +529,9 @@ public final class CraftMagicNumbers implements UnsafeValues {
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 +566,98 @@ public final class CraftMagicNumbers implements UnsafeValues {
}
@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");
net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag();
((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().serializeEntity(compound);
Set<EntitySerializationFlag> 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()
);
}
});
CompoundTag compound = new CompoundTag();
if (serializePassangers) {
if (!nmsEntity.saveAsPassenger(compound, true, includeNonSaveable, forceSerialization)) {
throw new IllegalArgumentException("Couldn't serialize entity");
}
} else {
List<net.minecraft.world.entity.Entity> 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
if (!preserveUUID) {
// Generate a new UUID so we don't have to worry about deserializing the same entity twice
compound.remove("UUID");
compound = PlatformHooks.get().convertNBT(References.ENTITY, MinecraftServer.getServer().fixerUpper, compound, dataVersion, this.getDataVersion()); // Paper - possibly use dataconverter
if (!preservePassengers) {
compound.remove("Passengers");
}
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 = deserializeEntity(compound, ((CraftWorld) world).getHandle(), preserveUUID);
return nmsEntity.getBukkitEntity();
}
private byte[] serializeNbtToBytes(net.minecraft.nbt.CompoundTag compound) {
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
compound.remove("UUID");
}
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(CompoundTag compound) {
compound.putInt("DataVersion", getDataVersion());
java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();
try {
@@ -597,8 +671,8 @@ public final class CraftMagicNumbers implements UnsafeValues {
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()

View File

@@ -613,8 +613,8 @@ public abstract class DelegatedGeneratorAccess implements WorldGenLevel {
}
@Override
public BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) {
return this.handle.clip(raytrace1, blockposition);
public BlockHitResult clip(ClipContext traverseContext, BlockPos traversePos) {
return this.handle.clip(traverseContext, traversePos);
}
@Override

View File

@@ -0,0 +1 @@
io.papermc.paper.PaperServerInternalAPIBridge

View File

@@ -0,0 +1 @@
io.papermc.paper.registry.data.InlinedRegistryBuilderProviderImpl

View File

@@ -22,6 +22,7 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.ShadowColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
@@ -278,6 +279,7 @@ class AdventureCodecsTest {
style(TextDecoration.BOLD.withState(TextDecoration.State.NOT_SET)),
style()
.font(key("kyori", "kittens"))
.shadowColor(ShadowColor.fromHexString("#FF00AAFF"))
.color(NamedTextColor.RED)
.decoration(TextDecoration.BOLD, true)
.clickEvent(openUrl("https://github.com"))

View File

@@ -25,7 +25,7 @@ public class RegistryLoadOrderTest {
private static boolean initInterface = false;
private static boolean initAbstract = false;
private static org.bukkit.Registry<Keyed> registry; // Paper - remap fix
private static Registry<Keyed> registry;
public static Stream<Arguments> data() {
return Stream.of(
@@ -60,6 +60,7 @@ public class RegistryLoadOrderTest {
RegistryLoadOrderTest.registry = new CraftRegistry<>(keyedClass, minecraftRegistry, minecraftToBukkit, (namespacedKey, apiVersion) -> namespacedKey);
this.testClassNotLoaded(init.get());
((CraftRegistry<?, ?>) RegistryLoadOrderTest.registry).lockReferenceHolders();
Object testOne = RegistryLoadOrderTest.registry.get(new NamespacedKey("bukkit", "test-one"));
Object otherTestOne = RegistryLoadOrderTest.registry.get(new NamespacedKey("bukkit", "test-one"));