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();
}
}