diff --git a/build-data/paper.at b/build-data/paper.at index 33797d11313e..6f710ff13536 100644 --- a/build-data/paper.at +++ b/build-data/paper.at @@ -99,6 +99,7 @@ public net.minecraft.util.datafix.fixes.BlockStateData register(ILcom/mojang/ser public net.minecraft.util.datafix.fixes.ItemIdFix ITEM_NAMES public net.minecraft.util.datafix.fixes.ItemSpawnEggFix ID_TO_ENTITY public net.minecraft.util.filefix.FileFixerUpper FILE_FIXER_INTRODUCTION_VERSION +public net.minecraft.util.valueproviders.WeightedListInt distribution public net.minecraft.world.BossEvent color public net.minecraft.world.BossEvent name public net.minecraft.world.BossEvent overlay @@ -630,6 +631,7 @@ public net.minecraft.world.level.levelgen.SurfaceRules$Context context public net.minecraft.world.level.levelgen.SurfaceRules$Context randomState public net.minecraft.world.level.levelgen.SurfaceRules$LazyCondition public net.minecraft.world.level.levelgen.SurfaceRules$LazyYCondition +public net.minecraft.world.level.levelgen.presets.WorldPreset dimensions public net.minecraft.world.level.levelgen.structure.StructurePiece SHAPE_CHECK_BLOCKS public net.minecraft.world.level.levelgen.structure.placement.StructurePlacement exclusionZone public net.minecraft.world.level.levelgen.structure.placement.StructurePlacement frequency diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/DimensionTypeKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/DimensionTypeKeys.java new file mode 100644 index 000000000000..496dfd1e46f1 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/DimensionTypeKeys.java @@ -0,0 +1,68 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.world.worldgen.DimensionType; +import net.kyori.adventure.key.Key; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#DIMENSION_TYPE}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class DimensionTypeKeys { + /** + * {@code minecraft:overworld} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey OVERWORLD = create(key("overworld")); + + /** + * {@code minecraft:overworld_caves} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey OVERWORLD_CAVES = create(key("overworld_caves")); + + /** + * {@code minecraft:the_end} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey THE_END = create(key("the_end")); + + /** + * {@code minecraft:the_nether} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey THE_NETHER = create(key("the_nether")); + + private DimensionTypeKeys() { + } + + /** + * Creates a typed key for {@link DimensionType} in the registry {@code minecraft:dimension_type}. + * + * @param key the value's key in the registry + * @return a new typed key + */ + public static TypedKey create(final Key key) { + return TypedKey.create(RegistryKey.DIMENSION_TYPE, key); + } +} diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/LevelStemKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/LevelStemKeys.java new file mode 100644 index 000000000000..758c43af567e --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/LevelStemKeys.java @@ -0,0 +1,40 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.world.worldgen.LevelStem; +import net.kyori.adventure.key.Key; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#LEVEL_STEM}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class LevelStemKeys { + private LevelStemKeys() { + } + + /** + * Creates a typed key for {@link LevelStem} in the registry {@code minecraft:dimension}. + * + * @param key the value's key in the registry + * @return a new typed key + */ + public static TypedKey create(final Key key) { + return TypedKey.create(RegistryKey.LEVEL_STEM, key); + } +} diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/WorldPresetKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/WorldPresetKeys.java new file mode 100644 index 000000000000..de071a5c3649 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/WorldPresetKeys.java @@ -0,0 +1,82 @@ +package io.papermc.paper.registry.keys; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.world.WorldPreset; +import net.kyori.adventure.key.Key; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla keys for {@link RegistryKey#WORLD_PRESET}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class WorldPresetKeys { + /** + * {@code minecraft:amplified} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey AMPLIFIED = create(key("amplified")); + + /** + * {@code minecraft:debug_all_block_states} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey DEBUG_ALL_BLOCK_STATES = create(key("debug_all_block_states")); + + /** + * {@code minecraft:flat} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey FLAT = create(key("flat")); + + /** + * {@code minecraft:large_biomes} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey LARGE_BIOMES = create(key("large_biomes")); + + /** + * {@code minecraft:normal} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey NORMAL = create(key("normal")); + + /** + * {@code minecraft:single_biome_surface} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TypedKey SINGLE_BIOME_SURFACE = create(key("single_biome_surface")); + + private WorldPresetKeys() { + } + + /** + * Creates a typed key for {@link WorldPreset} in the registry {@code minecraft:worldgen/world_preset}. + * + * @param key the value's key in the registry + * @return a new typed key + */ + public static TypedKey create(final Key key) { + return TypedKey.create(RegistryKey.WORLD_PRESET, key); + } +} diff --git a/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/WorldPresetTagKeys.java b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/WorldPresetTagKeys.java new file mode 100644 index 000000000000..a3de6c8ca841 --- /dev/null +++ b/paper-api/src/generated/java/io/papermc/paper/registry/keys/tags/WorldPresetTagKeys.java @@ -0,0 +1,54 @@ +package io.papermc.paper.registry.keys.tags; + +import static net.kyori.adventure.key.Key.key; + +import io.papermc.paper.annotation.GeneratedClass; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.world.WorldPreset; +import net.kyori.adventure.key.Key; +import org.jspecify.annotations.NullMarked; + +/** + * Vanilla tag keys for {@link RegistryKey#WORLD_PRESET}. + * + * @apiNote The fields provided here are a direct representation of + * what is available from the vanilla game source. They may be + * changed (including removals) on any Minecraft version + * bump, so cross-version compatibility is not provided on the + * same level as it is on most of the other API. + */ +@SuppressWarnings({ + "unused", + "SpellCheckingInspection" +}) +@NullMarked +@GeneratedClass +public final class WorldPresetTagKeys { + /** + * {@code #minecraft:extended} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey EXTENDED = create(key("extended")); + + /** + * {@code #minecraft:normal} + * + * @apiNote This field is version-dependant and may be removed in future Minecraft versions + */ + public static final TagKey NORMAL = create(key("normal")); + + private WorldPresetTagKeys() { + } + + /** + * Creates a tag key for {@link WorldPreset} in the registry {@code minecraft:worldgen/world_preset}. + * + * @param key the tag key's key + * @return a new tag key + */ + public static TagKey create(final Key key) { + return TagKey.create(RegistryKey.WORLD_PRESET, key); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/math/provider/IntProvider.java b/paper-api/src/main/java/io/papermc/paper/math/provider/IntProvider.java new file mode 100644 index 000000000000..b59d23a548fa --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/math/provider/IntProvider.java @@ -0,0 +1,291 @@ +package io.papermc.paper.math.provider; + +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; + +/** + * Represents a provider of a random integer. + */ +@ApiStatus.Experimental +public sealed interface IntProvider permits + IntProvider.Constant, + IntProvider.Uniform, + IntProvider.BiasedToBottom, + IntProvider.Clamped, + IntProvider.WeightedList, + IntProvider.ClampedNormal, + IntProvider.Trapezoid { + + /** + * Creates a constant provider of a given value. + * + * @param value the value to always return + * @return a constant provider of the given value + */ + @Contract(pure = true, value = "_ -> new") + static IntProvider.Constant constant(final int value) { + return IntProviderProvider.get().constant(value); + } + + /** + * Creates a uniform provider of a given range. + * + * @param min the minimum value of the range (inclusive) + * @param max the maximum value of the range (inclusive) + * @return a uniform provider of the given range + */ + @Contract(pure = true, value = "_, _ -> new") + static IntProvider.Uniform uniform(final int min, final int max) { + return IntProviderProvider.get().uniform(min, max); + } + + /** + * Creates a biased-to-bottom provider of a given range. + * + * @param min the minimum value of the range (inclusive) + * @param max the maximum value of the range (inclusive) + * @return a biased-to-bottom provider of the given range + */ + @Contract(pure = true, value = "_, _ -> new") + static IntProvider.BiasedToBottom biasedToBottom(final int min, final int max) { + return IntProviderProvider.get().biasedToBottom(min, max); + } + + /** + * Creates a clamped provider of a given source and range. + * + * @param source the source provider + * @param min the minimum value of the range (inclusive) + * @param max the maximum value of the range (inclusive) + * @return a clamped provider of the given source and range + */ + @Contract(pure = true, value = "_, _, _ -> new") + static IntProvider.Clamped clamped(final IntProvider source, final int min, final int max) { + return IntProviderProvider.get().clamped(source, min, max); + } + + /** + * Creates a weighted list provider of a given distribution. + * + * @param distribution the distribution of weighted providers + * @return a weighted list provider of the given distribution + */ + @Contract(pure = true, value = "_ -> new") + static IntProvider.WeightedList weightedList(final List distribution) { + return IntProviderProvider.get().weightedList(distribution); + } + + /** + * Creates a clamped-normal provider of a given mean, deviation, and range. + * + * @param mean the mean of the distribution + * @param deviation the standard deviation of the distribution + * @param min the minimum value of the range (inclusive) + * @param max the maximum value of the range (inclusive) + * @return a clamped-normal provider of the given mean, deviation, and range + */ + @Contract(pure = true, value = "_, _, _, _ -> new") + static IntProvider.ClampedNormal clampedNormal(final float mean, final float deviation, final int min, final int max) { + return IntProviderProvider.get().clampedNormal(mean, deviation, min, max); + } + + /** + * Creates a trapezoid provider of a given plateau and range. + * + * @param plateau the width of the plateau + * @param min the minimum value of the range (inclusive) + * @param max the maximum value of the range (inclusive) + * @return a trapezoid provider of the given range and plateau + */ + @Contract(pure = true, value = "_, _, _ -> new") + static IntProvider.Trapezoid trapezoid(final int plateau, final int min, final int max) { + return IntProviderProvider.get().trapezoid(plateau, min, max); + } + + /** + * Represents a constant provider of a single value. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + non-sealed interface Constant extends IntProvider { + + /** + * Returns the value of this constant provider. + * + * @return the value + */ + @Contract(pure = true) + int value(); + } + + /** + * Represents a uniform provider of a given range. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + non-sealed interface Uniform extends IntProvider { + + /** + * Returns the minimum value of the range of this uniform provider. + * + * @return the minimum value + */ + @Contract(pure = true) + int minInclusive(); + + /** + * Returns the maximum value of the range of this uniform provider. + * + * @return the maximum value + */ + @Contract(pure = true) + int maxInclusive(); + } + + /** + * Represents a biased-to-bottom provider of a given range. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + non-sealed interface BiasedToBottom extends IntProvider { + + /** + * Returns the minimum value of the range of this biased-to-bottom provider. + * + * @return the minimum value + */ + @Contract(pure = true) + int minInclusive(); + + /** + * Returns the maximum value of the range of this biased-to-bottom provider. + * + * @return the maximum value + */ + @Contract(pure = true) + int maxInclusive(); + } + + /** + * Represents a clamped provider of a given source and range. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + non-sealed interface Clamped extends IntProvider { + + /** + * Returns the source provider of this clamped provider. + * + * @return the source provider + */ + @Contract(pure = true) + IntProvider source(); + + /** + * Returns the minimum value of the range of this clamped provider. + * + * @return the minimum value + */ + @Contract(pure = true) + int minInclusive(); + + /** + * Returns the maximum value of the range of this clamped provider. + * + * @return the maximum value + */ + @Contract(pure = true) + int maxInclusive(); + } + + /** + * Represents a weighted list provider. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + non-sealed interface WeightedList extends IntProvider { + + /** + * Returns the distribution of this weighted list provider. + * + * @return the distribution + */ + @Contract(pure = true) + @Unmodifiable + List distribution(); + } + + /** + * Represents a clamped-normal provider of a given mean, deviation, and range. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + non-sealed interface ClampedNormal extends IntProvider { + + /** + * Returns the mean of the distribution of this clamped-normal provider. + * + * @return the mean of the distribution + */ + @Contract(pure = true) + float mean(); + + /** + * Returns the standard deviation of the distribution of this clamped-normal provider. + * + * @return the standard deviation + */ + @Contract(pure = true) + float deviation(); + + /** + * Returns the minimum value of the range of this clamped-normal provider. + * + * @return the minimum value + */ + @Contract(pure = true) + int minInclusive(); + + /** + * Returns the maximum value of the range of this clamped-normal provider. + * + * @return the maximum value + */ + @Contract(pure = true) + int maxInclusive(); + } + + /** + * Represents a trapezoid provider of a given plateau and range. + */ + @ApiStatus.Experimental + @ApiStatus.NonExtendable + non-sealed interface Trapezoid extends IntProvider { + + /** + * Returns the width of the plateau of this trapezoid provider. + * + * @return the width of the plateau + */ + @Contract(pure = true) + int plateau(); + + /** + * Returns the minimum value of the range of this trapezoid provider. + * + * @return the minimum value + */ + @Contract(pure = true) + int minInclusive(); + + /** + * Returns the maximum value of the range of this trapezoid provider. + * + * @return the maximum value + */ + @Contract(pure = true) + int maxInclusive(); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/math/provider/IntProviderProvider.java b/paper-api/src/main/java/io/papermc/paper/math/provider/IntProviderProvider.java new file mode 100644 index 000000000000..b137bfa510e4 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/math/provider/IntProviderProvider.java @@ -0,0 +1,30 @@ +package io.papermc.paper.math.provider; + +import java.util.List; +import java.util.Optional; +import java.util.ServiceLoader; + +interface IntProviderProvider { + + static IntProviderProvider get() { + final class Holder { + static final Optional INSTANCE = ServiceLoader.load(IntProviderProvider.class, IntProviderProvider.class.getClassLoader()).findFirst(); + } + + return Holder.INSTANCE.orElseThrow(); + } + + IntProvider.Constant constant(int value); + + IntProvider.Uniform uniform(int min, int max); + + IntProvider.BiasedToBottom biasedToBottom(int min, int max); + + IntProvider.Clamped clamped(IntProvider source, int min, int max); + + IntProvider.WeightedList weightedList(List distribution); + + IntProvider.ClampedNormal clampedNormal(float mean, float deviation, int min, int max); + + IntProvider.Trapezoid trapezoid(int plateau, int min, int max); +} diff --git a/paper-api/src/main/java/io/papermc/paper/math/provider/WeightedIntProvider.java b/paper-api/src/main/java/io/papermc/paper/math/provider/WeightedIntProvider.java new file mode 100644 index 000000000000..2c4399a92dec --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/math/provider/WeightedIntProvider.java @@ -0,0 +1,37 @@ +package io.papermc.paper.math.provider; + +import org.jetbrains.annotations.Contract; + +/** + * A weighted provider of a random integer. + */ +public sealed interface WeightedIntProvider permits WeightedIntProviderImpl { + + /** + * Creates a weighted provider of a given weight and provider. + * + * @param weight the weight + * @param provider the provider + * @return a weighted provider of the given weight and provider + */ + @Contract(pure = true, value = "_, _ -> new") + static WeightedIntProvider create(final int weight, final IntProvider provider) { + return new WeightedIntProviderImpl(weight, provider); + } + + /** + * Returns the weight of this weighted provider. + * + * @return the weight + */ + @Contract(pure = true) + int weight(); + + /** + * Returns the provider of this weighted provider. + * + * @return the provider + */ + @Contract(pure = true) + IntProvider provider(); +} diff --git a/paper-api/src/main/java/io/papermc/paper/math/provider/WeightedIntProviderImpl.java b/paper-api/src/main/java/io/papermc/paper/math/provider/WeightedIntProviderImpl.java new file mode 100644 index 000000000000..46acd4b7543e --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/math/provider/WeightedIntProviderImpl.java @@ -0,0 +1,4 @@ +package io.papermc.paper.math.provider; + +record WeightedIntProviderImpl(int weight, IntProvider provider) implements WeightedIntProvider { +} diff --git a/paper-api/src/main/java/io/papermc/paper/math/provider/package-info.java b/paper-api/src/main/java/io/papermc/paper/math/provider/package-info.java new file mode 100644 index 000000000000..033b262cbd7c --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/math/provider/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.math.provider; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java index aba87eec0445..6077b6f3a8b3 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKey.java @@ -4,6 +4,9 @@ import io.papermc.paper.dialog.Dialog; import io.papermc.paper.entity.poi.PoiType; import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.world.WorldPreset; +import io.papermc.paper.world.worldgen.DimensionType; +import io.papermc.paper.world.worldgen.LevelStem; import net.kyori.adventure.key.Key; import net.kyori.adventure.key.KeyPattern; import net.kyori.adventure.key.Keyed; @@ -140,6 +143,7 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { */ RegistryKey> GAME_RULE = create("game_rule"); + /* ********************** * * Data-driven Registries * * ********************** */ @@ -263,6 +267,21 @@ public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl { * @see io.papermc.paper.registry.keys.DialogKeys */ RegistryKey DIALOG = create("dialog"); + /** + * Data-driven registry for world presets. + * @see io.papermc.paper.registry.keys.WorldPresetKeys + */ + RegistryKey WORLD_PRESET = create("worldgen/world_preset"); + /** + * Data-driven registry for dimension types. + * @see io.papermc.paper.registry.keys.DimensionTypeKeys + */ + RegistryKey DIMENSION_TYPE = create("dimension_type"); + /** + * Data-driven registry for level stems. + * @see io.papermc.paper.registry.keys.LevelStemKeys + */ + RegistryKey LEVEL_STEM = create("dimension"); /* ******************* * diff --git a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java index ac68dfce754d..b0c6328b992b 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java @@ -3,6 +3,7 @@ import com.google.common.collect.Sets; import java.util.Set; import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; import org.intellij.lang.annotations.Subst; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -23,7 +24,7 @@ public int hashCode() { return System.identityHashCode(this); } - static RegistryKey create(@Subst("some_key") final String key) { + static RegistryKey create(@KeyPattern.Value final String key) { final RegistryKey registryKey = createInternal(key); REGISTRY_KEYS.add(registryKey); return registryKey; diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/DimensionTypeRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/DimensionTypeRegistryEntry.java new file mode 100644 index 000000000000..4978acc2230f --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/DimensionTypeRegistryEntry.java @@ -0,0 +1,105 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.math.provider.IntProvider; +import io.papermc.paper.registry.RegistryBuilder; +import io.papermc.paper.registry.set.RegistryKeySet; +import io.papermc.paper.world.worldgen.DimensionType; +import org.bukkit.block.BlockType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Range; + +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface DimensionTypeRegistryEntry { + + enum Skybox { + // Start generate - DimensionTypeSkybox + NONE, + OVERWORLD, + END; + // End generate - DimensionTypeSkybox + } + + enum CardinalLightType { + // Start generate - DimensionTypeCardinalLightType + DEFAULT, + NETHER; + // End generate - DimensionTypeCardinalLightType + } + + boolean hasFixedTime(); + + boolean hasSkyLight(); + + boolean hasCeiling(); + + boolean hasEnderDragonFight(); + + double coordinateScale(); + + @Range(from = -2032, to = 2031) int minY(); + + @Range(from = 16, to = 4064) int height(); + + @Range(from = 0, to = 4096) int logicalHeight(); + + RegistryKeySet infiniburn(); + + float ambientLight(); + + IntProvider monsterSpawnLightTest(); + + @Range(from = 0, to = 15) int monsterSpawnBlockLightLimit(); + + Skybox skybox(); + + CardinalLightType cardinalLightType(); + + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends RegistryBuilder { + + @Contract(value = "_ -> this", mutates = "this") + Builder hasFixedTime(boolean hasFixedTime); + + @Contract(value = "_ -> this", mutates = "this") + Builder hasSkyLight(boolean hasSkyLight); + + @Contract(value = "_ -> this", mutates = "this") + Builder hasCeiling(boolean hasCeiling); + + @Contract(value = "_ -> this", mutates = "this") + Builder hasEnderDragonFight(boolean hasEnderDragonFight); + + @Contract(value = "_ -> this", mutates = "this") + Builder coordinateScale(double coordinateScale); + + @Contract(value = "_ -> this", mutates = "this") + Builder minY(@Range(from = -2032, to = 2031) int minY); + + @Contract(value = "_ -> this", mutates = "this") + Builder height(@Range(from = 16, to = 4064) int height); + + @Contract(value = "_ -> this", mutates = "this") + Builder logicalHeight(@Range(from = 0, to = 4096) int logicalHeight); + + @Contract(value = "_ -> this", mutates = "this") + Builder infiniburn(RegistryKeySet infiniburn); + + @Contract(value = "_ -> this", mutates = "this") + Builder ambientLight(float ambientLight); + + @Contract(value = "_ -> this", mutates = "this") + Builder monsterSpawnLightTest(IntProvider monsterSpawnLightTest); + + @Contract(value = "_ -> this", mutates = "this") + Builder monsterSpawnBlockLightLimit(@Range(from = 0, to = 15) int monsterSpawnBlockLightLimit); + + @Contract(value = "_ -> this", mutates = "this") + Builder skybox(Skybox skybox); + + @Contract(value = "_ -> this", mutates = "this") + Builder cardinalLight(CardinalLightType cardinalLightType); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/data/LevelStemRegistryEntry.java b/paper-api/src/main/java/io/papermc/paper/registry/data/LevelStemRegistryEntry.java new file mode 100644 index 000000000000..c10eeb199e73 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/registry/data/LevelStemRegistryEntry.java @@ -0,0 +1,25 @@ +package io.papermc.paper.registry.data; + +import io.papermc.paper.registry.RegistryBuilder; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.world.WorldPreset; +import io.papermc.paper.world.worldgen.DimensionType; +import io.papermc.paper.world.worldgen.LevelStem; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +@ApiStatus.NonExtendable +public interface LevelStemRegistryEntry { + + // codec says it supports inlining, but the protocol does NOT (respawn packet only sends network id) + TypedKey type(); + + @ApiStatus.Experimental + @ApiStatus.NonExtendable + interface Builder extends RegistryBuilder { + + Builder type(TypedKey type); + + Builder copyGeneratorFrom(TypedKey preset, TypedKey levelStem); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java b/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java index cfe47a108b22..1ae4992b08b1 100644 --- a/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java +++ b/paper-api/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java @@ -7,11 +7,13 @@ import io.papermc.paper.registry.data.ChickenVariantRegistryEntry; import io.papermc.paper.registry.data.CowVariantRegistryEntry; import io.papermc.paper.registry.data.DamageTypeRegistryEntry; +import io.papermc.paper.registry.data.DimensionTypeRegistryEntry; import io.papermc.paper.registry.data.EnchantmentRegistryEntry; import io.papermc.paper.registry.data.FrogVariantRegistryEntry; import io.papermc.paper.registry.data.GameEventRegistryEntry; import io.papermc.paper.registry.data.InstrumentRegistryEntry; import io.papermc.paper.registry.data.JukeboxSongRegistryEntry; +import io.papermc.paper.registry.data.LevelStemRegistryEntry; import io.papermc.paper.registry.data.PaintingVariantRegistryEntry; import io.papermc.paper.registry.data.PigVariantRegistryEntry; import io.papermc.paper.registry.data.SulfurCubeArchetypeRegistryEntry; @@ -20,6 +22,8 @@ import io.papermc.paper.registry.data.WolfVariantRegistryEntry; import io.papermc.paper.registry.data.ZombieNautilusVariantRegistryEntry; import io.papermc.paper.registry.data.dialog.DialogRegistryEntry; +import io.papermc.paper.world.worldgen.DimensionType; +import io.papermc.paper.world.worldgen.LevelStem; import org.bukkit.Art; import org.bukkit.GameEvent; import org.bukkit.JukeboxSong; @@ -65,6 +69,8 @@ public final class RegistryEvents { public static final RegistryEventProvider ZOMBIE_NAUTILUS_VARIANT = create(RegistryKey.ZOMBIE_NAUTILUS_VARIANT); public static final RegistryEventProvider SULFUR_CUBE_ARCHETYPE = create(RegistryKey.SULFUR_CUBE_ARCHETYPE); public static final RegistryEventProvider DIALOG = create(RegistryKey.DIALOG); + public static final RegistryEventProvider DIMENSION_TYPE = create(RegistryKey.DIMENSION_TYPE); + public static final RegistryEventProvider LEVEL_STEM = create(RegistryKey.LEVEL_STEM); // End generate - RegistryEvents private RegistryEvents() { diff --git a/paper-api/src/main/java/io/papermc/paper/util/BoundChecker.java b/paper-api/src/main/java/io/papermc/paper/util/BoundChecker.java index 5d65576f750d..61b3dfb9f502 100644 --- a/paper-api/src/main/java/io/papermc/paper/util/BoundChecker.java +++ b/paper-api/src/main/java/io/papermc/paper/util/BoundChecker.java @@ -52,6 +52,27 @@ public static float requirePositive(final float value, final String field) { throw new IllegalArgumentException("argument " + field + " must be positive: " + value); } + public static double requireRange(final double value, final String field, final double min, final double max) { + if (Double.compare(value, min) >= 0 && Double.compare(value, max) <= 0) { + return value; + } + throw new IllegalArgumentException("argument " + field + " must be in [" + min + "," + max + "]: " + value); + } + + public static double requireNonNegative(final double value, final String field) { + if (Double.compare(value, 0.0F) >= 0 && Double.compare(value, Double.MAX_VALUE) <= 0) { + return value; + } + throw new IllegalArgumentException("argument " + field + " must be non-negative: " + value); + } + + public static double requirePositive(final double value, final String field) { + if (Double.compare(value, 0.0F) > 0 && Double.compare(value, Double.MAX_VALUE) <= 0) { + return value; + } + throw new IllegalArgumentException("argument " + field + " must be positive: " + value); + } + private BoundChecker() { } } diff --git a/paper-api/src/main/java/io/papermc/paper/world/MoonPhase.java b/paper-api/src/main/java/io/papermc/paper/world/MoonPhase.java index 0b24e1a92bba..ffe9279c3958 100644 --- a/paper-api/src/main/java/io/papermc/paper/world/MoonPhase.java +++ b/paper-api/src/main/java/io/papermc/paper/world/MoonPhase.java @@ -2,9 +2,7 @@ import java.util.HashMap; import java.util.Map; -import org.jspecify.annotations.NullMarked; -@NullMarked public enum MoonPhase { FULL_MOON(0L), WANING_GIBBOUS(1L), diff --git a/paper-api/src/main/java/io/papermc/paper/world/WorldPreset.java b/paper-api/src/main/java/io/papermc/paper/world/WorldPreset.java new file mode 100644 index 000000000000..d6db35e6f1d9 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/WorldPreset.java @@ -0,0 +1,6 @@ +package io.papermc.paper.world; + +import org.bukkit.Keyed; + +public interface WorldPreset extends Keyed { +} diff --git a/paper-api/src/main/java/io/papermc/paper/world/WorldPresets.java b/paper-api/src/main/java/io/papermc/paper/world/WorldPresets.java new file mode 100644 index 000000000000..22dc246402c8 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/WorldPresets.java @@ -0,0 +1,27 @@ +package io.papermc.paper.world; + +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; + +public final class WorldPresets { + + // Start generate - WorldPresets + public static final WorldPreset AMPLIFIED = getPreset("amplified"); + + public static final WorldPreset DEBUG_ALL_BLOCK_STATES = getPreset("debug_all_block_states"); + + public static final WorldPreset FLAT = getPreset("flat"); + + public static final WorldPreset LARGE_BIOMES = getPreset("large_biomes"); + + public static final WorldPreset NORMAL = getPreset("normal"); + + public static final WorldPreset SINGLE_BIOME_SURFACE = getPreset("single_biome_surface"); + // End generate - WorldPresets + + private static WorldPreset getPreset(@KeyPattern.Value final String key) { + return RegistryAccess.registryAccess().getRegistry(RegistryKey.WORLD_PRESET).getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key)); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/world/package-info.java b/paper-api/src/main/java/io/papermc/paper/world/package-info.java new file mode 100644 index 000000000000..7ddcb1ca1819 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.world; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-api/src/main/java/io/papermc/paper/world/worldgen/DimensionType.java b/paper-api/src/main/java/io/papermc/paper/world/worldgen/DimensionType.java new file mode 100644 index 000000000000..cbeec7fec27c --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/worldgen/DimensionType.java @@ -0,0 +1,6 @@ +package io.papermc.paper.world.worldgen; + +import org.bukkit.Keyed; + +public interface DimensionType extends Keyed { +} diff --git a/paper-api/src/main/java/io/papermc/paper/world/worldgen/DimensionTypes.java b/paper-api/src/main/java/io/papermc/paper/world/worldgen/DimensionTypes.java new file mode 100644 index 000000000000..4f2a08f32808 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/worldgen/DimensionTypes.java @@ -0,0 +1,23 @@ +package io.papermc.paper.world.worldgen; + +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; + +public final class DimensionTypes { + + // Start generate - DimensionTypes + public static final DimensionType OVERWORLD = getType("overworld"); + + public static final DimensionType OVERWORLD_CAVES = getType("overworld_caves"); + + public static final DimensionType THE_END = getType("the_end"); + + public static final DimensionType THE_NETHER = getType("the_nether"); + // End generate - DimensionTypes + + private static DimensionType getType(@KeyPattern.Value final String key) { + return RegistryAccess.registryAccess().getRegistry(RegistryKey.DIMENSION_TYPE).getOrThrow(Key.key(Key.MINECRAFT_NAMESPACE, key)); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/world/worldgen/LevelStem.java b/paper-api/src/main/java/io/papermc/paper/world/worldgen/LevelStem.java new file mode 100644 index 000000000000..06fba0e0288f --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/worldgen/LevelStem.java @@ -0,0 +1,8 @@ +package io.papermc.paper.world.worldgen; + +import org.bukkit.Keyed; + +public interface LevelStem extends Keyed { + + DimensionType getDimensionType(); +} diff --git a/paper-api/src/main/java/io/papermc/paper/world/worldgen/package-info.java b/paper-api/src/main/java/io/papermc/paper/world/worldgen/package-info.java new file mode 100644 index 000000000000..c18bf72f842d --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/world/worldgen/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.world.worldgen; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-generator/src/main/java/io/papermc/generator/Rewriters.java b/paper-generator/src/main/java/io/papermc/generator/Rewriters.java index b7424c58e28f..11dddb409711 100644 --- a/paper-generator/src/main/java/io/papermc/generator/Rewriters.java +++ b/paper-generator/src/main/java/io/papermc/generator/Rewriters.java @@ -29,7 +29,10 @@ import io.papermc.paper.entity.poi.PoiTypes; import io.papermc.paper.item.MapPostProcessing; import io.papermc.paper.entity.RemovalReason; +import io.papermc.paper.registry.data.DimensionTypeRegistryEntry; import io.papermc.paper.world.WeatheringCopperState; +import io.papermc.paper.world.WorldPresets; +import io.papermc.paper.world.worldgen.DimensionTypes; import io.papermc.typewriter.preset.EnumCloneRewriter; import io.papermc.typewriter.preset.model.EnumValue; import io.papermc.typewriter.replace.SearchMetadata; @@ -47,7 +50,9 @@ import net.minecraft.world.entity.player.ChatVisiblity; import net.minecraft.world.item.Rarity; import net.minecraft.world.item.SwingAnimationType; +import net.minecraft.world.level.CardinalLighting; import net.minecraft.world.level.block.WeatheringCopper; +import net.minecraft.world.level.dimension.DimensionType; import org.bukkit.Art; import org.bukkit.FeatureFlag; import org.bukkit.Fluid; @@ -154,6 +159,10 @@ protected EnumValue.Builder rewriteEnumValue(net.minecraft.world.entity.animal.p .register("AttributeSentiment", Attribute.Sentiment.class, new EnumCloneRewriter<>(net.minecraft.world.entity.ai.attributes.Attribute.Sentiment.class)) .register("WeatheringCopperState", WeatheringCopperState.class, new EnumCloneRewriter<>(WeatheringCopper.WeatherState.class)) .register("CopperGolemState", CopperGolem.State.class, new EnumCloneRewriter<>(net.minecraft.world.entity.animal.golem.CopperGolemState.class)) + .register(DimensionTypeRegistryEntry.class, composite( + holder("DimensionTypeSkybox", DimensionTypeRegistryEntry.Skybox.class, new EnumCloneRewriter<>(DimensionType.Skybox.class)), + holder("DimensionTypeCardinalLightType", DimensionTypeRegistryEntry.CardinalLightType.class, new EnumCloneRewriter<>(CardinalLighting.Type.class)) + )) .register(ClientOption.class, composite( holder("ChatVisibility", ClientOption.ChatVisibility.class, new EnumCloneRewriter<>(ChatVisiblity.class) { @Override @@ -242,6 +251,8 @@ protected String rewriteFieldType(Holder.Reference(Registries.SULFUR_CUBE_ARCHETYPE, "getArchetype")) .register("Dialog", Dialog.class, new RegistryFieldRewriter<>(Registries.DIALOG, "getDialog")) .register("PoiTypes", PoiTypes.class, new RegistryFieldRewriter<>(Registries.POINT_OF_INTEREST_TYPE, "get")) + .register("WorldPresets", WorldPresets.class, new RegistryFieldRewriter<>(Registries.WORLD_PRESET, "getPreset")) + .register("DimensionTypes", DimensionTypes.class, new RegistryFieldRewriter<>(Registries.DIMENSION_TYPE, "getType")) .register("MemoryKey", MemoryKey.class, new MemoryKeyRewriter()) // .register("ItemType", org.bukkit.inventory.ItemType.class, new io.papermc.generator.rewriter.types.simple.ItemTypeRewriter()) // - disable for now, lynx want the generic type .register("BlockType", BlockType.class, new BlockTypeRewriter()) diff --git a/paper-generator/src/main/java/io/papermc/generator/registry/RegistryBootstrapper.java b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryBootstrapper.java index e5c6f4e77219..ea285d74c2c9 100644 --- a/paper-generator/src/main/java/io/papermc/generator/registry/RegistryBootstrapper.java +++ b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryBootstrapper.java @@ -20,15 +20,17 @@ public class RegistryBootstrapper { public static void bootstrap(List generators) { // typed/tag keys RegistryEntries.forEach(entry -> { - generators.add(new GeneratedKeyType<>(PAPER_REGISTRY_PACKAGE + ".keys", entry)); + if (entry.registry().listElements().findAny().isPresent() || entry.allowCustomKeys()) { + generators.add(new GeneratedKeyType<>(PAPER_REGISTRY_PACKAGE + ".keys", entry)); + } if (entry.registry().listTags().findAny().isPresent()) { - generators.add(new GeneratedTagKeyType(entry, PAPER_REGISTRY_PACKAGE + ".keys.tags")); + generators.add(new GeneratedTagKeyType<>(entry, PAPER_REGISTRY_PACKAGE + ".keys.tags")); } }); // todo remove once entity type and potion are both a registry - generators.add(new GeneratedTagKeyType(RegistryEntries.byRegistryKey(Registries.ENTITY_TYPE), PAPER_REGISTRY_PACKAGE + ".keys.tags")); - generators.add(new GeneratedTagKeyType(RegistryEntries.byRegistryKey(Registries.POTION), PAPER_REGISTRY_PACKAGE + ".keys.tags")); + generators.add(new GeneratedTagKeyType<>(RegistryEntries.byRegistryKey(Registries.ENTITY_TYPE), PAPER_REGISTRY_PACKAGE + ".keys.tags")); + generators.add(new GeneratedTagKeyType<>(RegistryEntries.byRegistryKey(Registries.POTION), PAPER_REGISTRY_PACKAGE + ".keys.tags")); } public static void bootstrap(PatternSourceSetRewriter apiSourceSet, PatternSourceSetRewriter serverSourceSet) { diff --git a/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java index 3546d56e61e0..451bb867d28a 100644 --- a/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java +++ b/paper-generator/src/main/java/io/papermc/generator/registry/RegistryEntries.java @@ -10,11 +10,13 @@ import io.papermc.paper.registry.data.ChickenVariantRegistryEntry; import io.papermc.paper.registry.data.CowVariantRegistryEntry; import io.papermc.paper.registry.data.DamageTypeRegistryEntry; +import io.papermc.paper.registry.data.DimensionTypeRegistryEntry; import io.papermc.paper.registry.data.EnchantmentRegistryEntry; import io.papermc.paper.registry.data.FrogVariantRegistryEntry; import io.papermc.paper.registry.data.GameEventRegistryEntry; import io.papermc.paper.registry.data.InstrumentRegistryEntry; import io.papermc.paper.registry.data.JukeboxSongRegistryEntry; +import io.papermc.paper.registry.data.LevelStemRegistryEntry; import io.papermc.paper.registry.data.PaintingVariantRegistryEntry; import io.papermc.paper.registry.data.PigVariantRegistryEntry; import io.papermc.paper.registry.data.SoundEventRegistryEntry; @@ -24,6 +26,10 @@ import io.papermc.paper.registry.data.WolfVariantRegistryEntry; import io.papermc.paper.registry.data.ZombieNautilusVariantRegistryEntry; import io.papermc.paper.registry.data.dialog.DialogRegistryEntry; +import io.papermc.paper.world.WorldPreset; +import io.papermc.paper.world.worldgen.DimensionType; +import io.papermc.paper.world.worldgen.DimensionTypes; +import io.papermc.paper.world.worldgen.LevelStem; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; @@ -73,7 +79,9 @@ import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BannerPatterns; +import net.minecraft.world.level.dimension.BuiltinDimensionTypes; import net.minecraft.world.level.gamerules.GameRules; +import net.minecraft.world.level.levelgen.presets.WorldPresets; import net.minecraft.world.level.levelgen.structure.BuiltinStructures; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.saveddata.maps.MapDecorationTypes; @@ -209,7 +217,10 @@ private static RegistryEntry inconsistentEntry(ResourceKey> API_ONLY = List.of( diff --git a/paper-generator/src/main/java/io/papermc/generator/types/registry/GeneratedKeyType.java b/paper-generator/src/main/java/io/papermc/generator/types/registry/GeneratedKeyType.java index a69ac7e3afad..9677898cd061 100644 --- a/paper-generator/src/main/java/io/papermc/generator/types/registry/GeneratedKeyType.java +++ b/paper-generator/src/main/java/io/papermc/generator/types/registry/GeneratedKeyType.java @@ -117,6 +117,10 @@ protected TypeSpec getTypeSpec() { typeBuilder.addField(fieldBuilder.build()); } + if (allExperimental && typeBuilder.fieldSpecs.isEmpty()) { + allExperimental = false; + } + if (allExperimental) { typeBuilder.addAnnotation(EXPERIMENTAL_API_ANNOTATION); createMethod.addAnnotation(EXPERIMENTAL_API_ANNOTATION); diff --git a/paper-generator/src/main/java/io/papermc/generator/types/registry/GeneratedTagKeyType.java b/paper-generator/src/main/java/io/papermc/generator/types/registry/GeneratedTagKeyType.java index 1d8810710c81..e8b49f7d49a9 100644 --- a/paper-generator/src/main/java/io/papermc/generator/types/registry/GeneratedTagKeyType.java +++ b/paper-generator/src/main/java/io/papermc/generator/types/registry/GeneratedTagKeyType.java @@ -18,7 +18,6 @@ import io.papermc.generator.utils.experimental.SingleFlagHolder; import io.papermc.paper.registry.RegistryKey; import io.papermc.paper.registry.tag.TagKey; -import java.util.concurrent.atomic.AtomicBoolean; import net.kyori.adventure.key.Key; import org.jspecify.annotations.NullMarked; @@ -31,11 +30,11 @@ import static javax.lang.model.element.Modifier.STATIC; @NullMarked -public class GeneratedTagKeyType extends SimpleGenerator { +public class GeneratedTagKeyType extends SimpleGenerator { - private final RegistryEntry entry; + private final RegistryEntry entry; - public GeneratedTagKeyType(RegistryEntry entry, String packageName) { + public GeneratedTagKeyType(RegistryEntry entry, String packageName) { super(entry.keyClassName().concat("TagKeys"), packageName); this.entry = entry; } @@ -83,8 +82,8 @@ protected TypeSpec getTypeSpec() { TypeSpec.Builder typeBuilder = this.keyHolderType(); MethodSpec.Builder createMethod = this.createMethod(tagKeyType); - AtomicBoolean allExperimental = new AtomicBoolean(true); - this.entry.registry().listTagIds().sorted(Formatting.TAG_ORDER).forEach(tagKey -> { + boolean allExperimental = true; + for (net.minecraft.tags.TagKey tagKey : this.entry.registry().listTagIds().sorted(Formatting.TAG_ORDER).toList()) { String fieldName = Formatting.formatKeyAsField(tagKey.location().getPath()); FieldSpec.Builder fieldBuilder = FieldSpec.builder(tagKeyType, fieldName, PUBLIC, STATIC, FINAL) .initializer("$N(key($S))", createMethod.build(), tagKey.location().getPath()) @@ -94,11 +93,16 @@ protected TypeSpec getTypeSpec() { if (featureFlagName != null) { fieldBuilder.addAnnotations(experimentalAnnotations(SingleFlagHolder.fromName(featureFlagName))); } else { - allExperimental.set(false); + allExperimental = false; } typeBuilder.addField(fieldBuilder.build()); - }); - if (allExperimental.get()) { + } + + if (allExperimental && typeBuilder.fieldSpecs.isEmpty()) { + allExperimental = false; + } + + if (allExperimental) { typeBuilder.addAnnotation(EXPERIMENTAL_API_ANNOTATION); createMethod.addAnnotation(EXPERIMENTAL_API_ANNOTATION); } diff --git a/paper-server/src/main/java/io/papermc/paper/math/provider/PaperIntProvider.java b/paper-server/src/main/java/io/papermc/paper/math/provider/PaperIntProvider.java new file mode 100644 index 000000000000..6f2a1666ad54 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/math/provider/PaperIntProvider.java @@ -0,0 +1,135 @@ +package io.papermc.paper.math.provider; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.util.random.Weighted; +import net.minecraft.util.valueproviders.BiasedToBottomInt; +import net.minecraft.util.valueproviders.ClampedInt; +import net.minecraft.util.valueproviders.ClampedNormalInt; +import net.minecraft.util.valueproviders.ConstantInt; +import net.minecraft.util.valueproviders.TrapezoidInt; +import net.minecraft.util.valueproviders.UniformInt; +import net.minecraft.util.valueproviders.WeightedListInt; +import org.jetbrains.annotations.Unmodifiable; + +public interface PaperIntProvider { + + static IntProvider fromVanilla(final net.minecraft.util.valueproviders.IntProvider provider) { + return switch (provider) { + case final ConstantInt constantInt -> new PaperConstant(constantInt); + case final UniformInt uniformInt -> new PaperUniform(uniformInt); + case final BiasedToBottomInt biasedToBottomInt -> new PaperBiasedToBottom(biasedToBottomInt); + case final ClampedInt clampedInt -> new PaperClamped(clampedInt); + case final WeightedListInt weightedListInt -> new PaperWeightedList(weightedListInt); + case final ClampedNormalInt clampedNormalInt -> new PaperClampedNormal(clampedNormalInt); + case final TrapezoidInt trapezoidInt -> new PaperTrapezoid(trapezoidInt); + default -> throw new IllegalArgumentException("Unknown int provider type: " + BuiltInRegistries.INT_PROVIDER_TYPE.getKey(provider.codec())); + }; + } + + static net.minecraft.util.valueproviders.IntProvider toVanilla(final IntProvider provider) { + return ((PaperIntProvider) provider).handle(); + } + + net.minecraft.util.valueproviders.IntProvider handle(); + + record PaperConstant(ConstantInt handle) implements IntProvider.Constant, PaperIntProvider { + @Override + public int value() { + return this.handle.value(); + } + } + + record PaperUniform(UniformInt handle) implements IntProvider.Uniform, PaperIntProvider { + @Override + public int minInclusive() { + return this.handle.minInclusive(); + } + + @Override + public int maxInclusive() { + return this.handle.maxInclusive(); + } + } + + record PaperBiasedToBottom(BiasedToBottomInt handle) implements IntProvider.BiasedToBottom, PaperIntProvider { + @Override + public int minInclusive() { + return this.handle.minInclusive(); + } + + @Override + public int maxInclusive() { + return this.handle.maxInclusive(); + } + } + + record PaperClamped(ClampedInt handle) implements IntProvider.Clamped, PaperIntProvider { + @Override + public IntProvider source() { + return PaperIntProvider.fromVanilla(this.handle.source()); + } + + @Override + public int minInclusive() { + return this.handle.minInclusive(); + } + + @Override + public int maxInclusive() { + return this.handle.maxInclusive(); + } + } + + record PaperWeightedList(WeightedListInt handle) implements IntProvider.WeightedList, PaperIntProvider { + + @Override + public @Unmodifiable List distribution() { + final ImmutableList.Builder builder = ImmutableList.builder(); + for (final Weighted weighted : this.handle.distribution.unwrap()) { + builder.add(WeightedIntProvider.create(weighted.weight(), PaperIntProvider.fromVanilla(weighted.value()))); + } + return builder.build(); + } + } + + record PaperClampedNormal(ClampedNormalInt handle) implements IntProvider.ClampedNormal, PaperIntProvider { + @Override + public float mean() { + return this.handle.mean(); + } + + @Override + public float deviation() { + return this.handle.deviation(); + } + + @Override + public int minInclusive() { + return this.handle.minInclusive(); + } + + @Override + public int maxInclusive() { + return this.handle.maxInclusive(); + } + } + + record PaperTrapezoid(TrapezoidInt handle) implements IntProvider.Trapezoid, PaperIntProvider { + @Override + public int plateau() { + return this.handle.plateau(); + } + + @Override + public int minInclusive() { + return this.handle.minInclusive(); + } + + @Override + public int maxInclusive() { + return this.handle.maxInclusive(); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/math/provider/PaperIntProviderProvider.java b/paper-server/src/main/java/io/papermc/paper/math/provider/PaperIntProviderProvider.java new file mode 100644 index 000000000000..69a06f92b6ce --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/math/provider/PaperIntProviderProvider.java @@ -0,0 +1,61 @@ +package io.papermc.paper.math.provider; + +import com.google.common.base.Preconditions; +import java.util.List; +import net.minecraft.util.random.WeightedList; +import net.minecraft.util.valueproviders.BiasedToBottomInt; +import net.minecraft.util.valueproviders.ClampedInt; +import net.minecraft.util.valueproviders.ClampedNormalInt; +import net.minecraft.util.valueproviders.ConstantInt; +import net.minecraft.util.valueproviders.TrapezoidInt; +import net.minecraft.util.valueproviders.UniformInt; +import net.minecraft.util.valueproviders.WeightedListInt; + +public class PaperIntProviderProvider implements IntProviderProvider { + + @Override + public IntProvider.Constant constant(final int value) { + return new PaperIntProvider.PaperConstant(ConstantInt.of(value)); + } + + @Override + public IntProvider.Uniform uniform(final int min, final int max) { + Preconditions.checkArgument(min <= max, "min <= max"); + return new PaperIntProvider.PaperUniform(UniformInt.of(min, max)); + } + + @Override + public IntProvider.BiasedToBottom biasedToBottom(final int min, final int max) { + Preconditions.checkArgument(min <= max, "min <= max"); + return new PaperIntProvider.PaperBiasedToBottom(BiasedToBottomInt.of(min, max)); + } + + @Override + public IntProvider.Clamped clamped(final IntProvider source, final int min, final int max) { + Preconditions.checkArgument(min <= max, "min <= max"); + return new PaperIntProvider.PaperClamped(ClampedInt.of(PaperIntProvider.toVanilla(source), min, max)); + } + + @Override + public IntProvider.WeightedList weightedList(final List distribution) { + Preconditions.checkArgument(!distribution.isEmpty(), "distribution cannot be empty"); + final WeightedList.Builder builder = WeightedList.builder(); + for (final WeightedIntProvider weightedIntProvider : distribution) { + builder.add(PaperIntProvider.toVanilla(weightedIntProvider.provider()), weightedIntProvider.weight()); + } + return new PaperIntProvider.PaperWeightedList(new WeightedListInt(builder.build())); + } + + @Override + public IntProvider.ClampedNormal clampedNormal(final float mean, final float deviation, final int min, final int max) { + Preconditions.checkArgument(min <= max, "min <= max"); + return new PaperIntProvider.PaperClampedNormal(ClampedNormalInt.of(mean, deviation, min, max)); + } + + @Override + public IntProvider.Trapezoid trapezoid(final int plateau, final int min, final int max) { + Preconditions.checkArgument(min <= max, "min <= max"); + Preconditions.checkArgument(plateau <= max - min, "plateau <= max - min"); + return new PaperIntProvider.PaperTrapezoid(TrapezoidInt.of(min, max, plateau)); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/math/provider/package-info.java b/paper-server/src/main/java/io/papermc/paper/math/provider/package-info.java new file mode 100644 index 000000000000..033b262cbd7c --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/math/provider/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.papermc.paper.math.provider; + +import org.jspecify.annotations.NullMarked; diff --git a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java index 17cf4ecbd97b..8e10aaf03aff 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/PaperRegistries.java @@ -13,11 +13,13 @@ import io.papermc.paper.registry.data.PaperChickenVariantRegistryEntry; import io.papermc.paper.registry.data.PaperCowVariantRegistryEntry; import io.papermc.paper.registry.data.PaperDamageTypeRegistryEntry; +import io.papermc.paper.registry.data.PaperDimensionTypeRegistryEntry; import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry; import io.papermc.paper.registry.data.PaperFrogVariantRegistryEntry; import io.papermc.paper.registry.data.PaperGameEventRegistryEntry; import io.papermc.paper.registry.data.PaperInstrumentRegistryEntry; import io.papermc.paper.registry.data.PaperJukeboxSongRegistryEntry; +import io.papermc.paper.registry.data.PaperLevelStemRegistryEntry; import io.papermc.paper.registry.data.PaperPaintingVariantRegistryEntry; import io.papermc.paper.registry.data.PaperPigVariantRegistryEntry; import io.papermc.paper.registry.data.PaperSoundEventRegistryEntry; @@ -30,6 +32,14 @@ import io.papermc.paper.registry.entry.RegistryEntry; import io.papermc.paper.registry.entry.RegistryEntryMeta; import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.world.PaperWorldPreset; +import io.papermc.paper.world.WorldPreset; +import io.papermc.paper.world.WorldPresets; +import io.papermc.paper.world.worldgen.DimensionType; +import io.papermc.paper.world.worldgen.DimensionTypes; +import io.papermc.paper.world.worldgen.LevelStem; +import io.papermc.paper.world.worldgen.PaperDimensionType; +import io.papermc.paper.world.worldgen.PaperLevelStem; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; @@ -155,6 +165,9 @@ public final class PaperRegistries { start(Registries.ZOMBIE_NAUTILUS_VARIANT, RegistryKey.ZOMBIE_NAUTILUS_VARIANT).craft(ZombieNautilus.Variant.class, CraftZombieNautilus.CraftVariant::new).writable(PaperZombieNautilusVariantRegistryEntry.PaperBuilder::new), start(Registries.SULFUR_CUBE_ARCHETYPE, RegistryKey.SULFUR_CUBE_ARCHETYPE).craft(SulfurCube.Archetype.class, CraftSulfurCube.CraftArchetype::new).writable(PaperSulfurCubeArchetypeRegistryEntry.PaperBuilder::new), start(Registries.DIALOG, RegistryKey.DIALOG).craft(Dialog.class, PaperDialog::new, true).writable(PaperDialogRegistryEntry.PaperBuilder::new), + start(Registries.WORLD_PRESET, RegistryKey.WORLD_PRESET).craft(WorldPresets.class, PaperWorldPreset::new).build(), + start(Registries.DIMENSION_TYPE, RegistryKey.DIMENSION_TYPE).craft(DimensionTypes.class, PaperDimensionType::new).writable(PaperDimensionTypeRegistryEntry.PaperBuilder::new), + start(Registries.LEVEL_STEM, RegistryKey.LEVEL_STEM).craft(LevelStem.class, PaperLevelStem::new).writable(PaperLevelStemRegistryEntry.PaperBuilder::new), // api-only start(Registries.ENTITY_TYPE, RegistryKey.ENTITY_TYPE).apiOnly(PaperSimpleRegistry::entityType), diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperDimensionTypeRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperDimensionTypeRegistryEntry.java new file mode 100644 index 000000000000..dcf44b5e51b8 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperDimensionTypeRegistryEntry.java @@ -0,0 +1,266 @@ +package io.papermc.paper.registry.data; + +import com.google.common.base.Preconditions; +import io.papermc.paper.math.provider.IntProvider; +import io.papermc.paper.math.provider.PaperIntProvider; +import io.papermc.paper.registry.PaperRegistryBuilder; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.util.Conversions; +import io.papermc.paper.registry.set.PaperRegistrySets; +import io.papermc.paper.registry.set.RegistryKeySet; +import java.util.Optional; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.Registries; +import net.minecraft.util.Mth; +import net.minecraft.world.attribute.EnvironmentAttributeMap; +import net.minecraft.world.clock.WorldClock; +import net.minecraft.world.level.CardinalLighting; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.lighting.LightEngine; +import net.minecraft.world.timeline.Timeline; +import org.bukkit.block.BlockType; +import org.jetbrains.annotations.Range; +import org.jspecify.annotations.Nullable; + +import static io.papermc.paper.registry.data.util.Checks.asArgument; +import static io.papermc.paper.registry.data.util.Checks.asConfigured; +import static io.papermc.paper.util.BoundChecker.requireRange; + +public class PaperDimensionTypeRegistryEntry implements DimensionTypeRegistryEntry { + + protected final Conversions conversions; + protected boolean hasFixedTime = false; + protected @Nullable Boolean hasSkylight; + protected @Nullable Boolean hasCeiling; + protected @Nullable Boolean hasEnderDragonFight; + protected @Nullable Double coordinateScale; + protected @Nullable Integer minY; + protected @Nullable Integer height; + protected @Nullable Integer logicalHeight; + protected @Nullable HolderSet infiniburn; + protected @Nullable Float ambientLight; + protected net.minecraft.util.valueproviders.@Nullable IntProvider monsterSpawnLightTest; + protected @Nullable Integer monsterSpawnBlockLightLimit; + protected DimensionType.Skybox skybox = DimensionType.Skybox.OVERWORLD; + protected CardinalLighting.Type cardinalLightType = CardinalLighting.Type.DEFAULT; + protected EnvironmentAttributeMap attributes = EnvironmentAttributeMap.EMPTY; + protected HolderSet timelines = HolderSet.empty(); + protected Optional> defaultClock = Optional.empty(); + + public PaperDimensionTypeRegistryEntry(final Conversions conversions, final @Nullable DimensionType internal) { + this.conversions = conversions; + if (internal == null) { + return; + } + + this.hasFixedTime = internal.hasFixedTime(); + this.hasSkylight = internal.hasSkyLight(); + this.hasCeiling = internal.hasCeiling(); + this.hasEnderDragonFight = internal.hasEnderDragonFight(); + this.coordinateScale = internal.coordinateScale(); + this.minY = internal.minY(); + this.height = internal.height(); + this.logicalHeight = internal.logicalHeight(); + this.infiniburn = internal.infiniburn(); + this.ambientLight = internal.ambientLight(); + this.monsterSpawnLightTest = internal.monsterSettings().monsterSpawnLightTest(); + this.monsterSpawnBlockLightLimit = internal.monsterSettings().monsterSpawnBlockLightLimit(); + this.skybox = internal.skybox(); + this.cardinalLightType = internal.cardinalLightType(); + this.attributes = internal.attributes(); + this.timelines = internal.timelines(); + this.defaultClock = internal.defaultClock(); + } + + @Override + public boolean hasFixedTime() { + return this.hasFixedTime; + } + + @Override + public boolean hasSkyLight() { + return asConfigured(this.hasSkylight, "hasSkyLight"); + } + + @Override + public boolean hasCeiling() { + return asConfigured(this.hasCeiling, "hasCeiling"); + } + + @Override + public boolean hasEnderDragonFight() { + return asConfigured(this.hasEnderDragonFight, "hasEnderDragonFight"); + } + + @Override + public double coordinateScale() { + return asConfigured(this.coordinateScale, "coordinateScale"); + } + + @Override + public @Range(from = DimensionType.MIN_Y, to = DimensionType.MAX_Y) int minY() { + return asConfigured(this.minY, "minY"); + } + + @Override + public @Range(from = DimensionType.MIN_HEIGHT, to = DimensionType.Y_SIZE) int height() { + return asConfigured(this.height, "height"); + } + + @Override + public @Range(from = 0, to = DimensionType.Y_SIZE) int logicalHeight() { + return asConfigured(this.logicalHeight, "logicalHeight"); + } + + @Override + public RegistryKeySet infiniburn() { + return PaperRegistrySets.convertToApi(RegistryKey.BLOCK, asConfigured(this.infiniburn, "infiniburn")); + } + + @Override + public float ambientLight() { + return asConfigured(this.ambientLight, "ambientLight"); + } + + @Override + public IntProvider monsterSpawnLightTest() { + return PaperIntProvider.fromVanilla(asConfigured(this.monsterSpawnLightTest, "monsterSpawnLightTest")); + } + + @Override + public @Range(from = 0, to = LightEngine.MAX_LEVEL) int monsterSpawnBlockLightLimit() { + return asConfigured(this.monsterSpawnBlockLightLimit, "monsterSpawnBlockLightLimit"); + } + + @Override + public Skybox skybox() { + return Skybox.valueOf(this.skybox.name()); + } + + @Override + public CardinalLightType cardinalLightType() { + return CardinalLightType.valueOf(this.cardinalLightType.name()); + } + + public static final class PaperBuilder extends PaperDimensionTypeRegistryEntry implements DimensionTypeRegistryEntry.Builder, PaperRegistryBuilder { + + public PaperBuilder(final Conversions conversions, final @Nullable DimensionType internal) { + super(conversions, internal); + } + + @Override + public Builder hasFixedTime(final boolean hasFixedTime) { + this.hasFixedTime = hasFixedTime; + return this; + } + + @Override + public Builder hasSkyLight(final boolean hasSkyLight) { + this.hasSkylight = hasSkyLight; + return this; + } + + @Override + public Builder hasCeiling(final boolean hasCeiling) { + this.hasCeiling = hasCeiling; + return this; + } + + @Override + public Builder hasEnderDragonFight(final boolean hasEnderDragonFight) { + this.hasEnderDragonFight = hasEnderDragonFight; + return this; + } + + @Override + public Builder coordinateScale(final double coordinateScale) { + this.coordinateScale = requireRange(coordinateScale, "coordinateScale", 1.0E-5F, 3.0E7); + return this; + } + + @Override + public Builder minY(final @Range(from = DimensionType.MIN_Y, to = DimensionType.MAX_Y) int minY) { + Preconditions.checkArgument(Mth.isMultipleOf(minY, 16), "minY has to be multiple of 16"); + this.minY = requireRange(minY, "minY", DimensionType.MIN_Y, DimensionType.MAX_Y); + return this; + } + + @Override + public Builder height(final @Range(from = DimensionType.MIN_HEIGHT, to = DimensionType.Y_SIZE) int height) { + Preconditions.checkArgument(Mth.isMultipleOf(height, 16), "height has to be multiple of 16"); + this.height = requireRange(height, "height", DimensionType.MIN_HEIGHT, DimensionType.Y_SIZE); + return this; + } + + @Override + public Builder logicalHeight(final @Range(from = 0, to = DimensionType.Y_SIZE) int logicalHeight) { + this.logicalHeight = requireRange(logicalHeight, "logicalHeight", 0, DimensionType.Y_SIZE); + return this; + } + + @Override + public Builder infiniburn(final RegistryKeySet infiniburn) { + this.infiniburn = PaperRegistrySets.convertToNms(Registries.BLOCK, this.conversions.lookup(), asArgument(infiniburn, "infiniburn")); + return this; + } + + @Override + public Builder ambientLight(final float ambientLight) { + this.ambientLight = ambientLight; + return this; + } + + @Override + public Builder monsterSpawnLightTest(final IntProvider monsterSpawnLightTest) { + final var temp = PaperIntProvider.toVanilla(asArgument(monsterSpawnLightTest, "monsterSpawnLightTest")); + Preconditions.checkArgument(temp.minInclusive() < 0 || temp.maxInclusive() > LightEngine.MAX_LEVEL, "monsterSpawnLightTest must be in the range [0, %s]", LightEngine.MAX_LEVEL); + this.monsterSpawnLightTest = temp; + return this; + } + + @Override + public Builder monsterSpawnBlockLightLimit(final @Range(from = 0, to = LightEngine.MAX_LEVEL) int monsterSpawnBlockLightLimit) { + this.monsterSpawnBlockLightLimit = requireRange(monsterSpawnBlockLightLimit, "monsterSpawnBlockLightLimit", 0, LightEngine.MAX_LEVEL); + return this; + } + + @Override + public Builder skybox(final Skybox skybox) { + this.skybox = DimensionType.Skybox.valueOf(asArgument(skybox, "skybox").name()); + return this; + } + + @Override + public Builder cardinalLight(final CardinalLightType cardinalLightType) { + this.cardinalLightType = CardinalLighting.Type.valueOf(asArgument(cardinalLightType, "cardinalLightType").name()); + return this; + } + + @Override + public DimensionType build() { + return new DimensionType( + this.hasFixedTime, + this.hasSkyLight(), + this.hasCeiling(), + this.hasEnderDragonFight(), + this.coordinateScale(), + this.minY(), + this.height(), + this.logicalHeight(), + asConfigured(this.infiniburn, "infiniburn"), + this.ambientLight(), + new DimensionType.MonsterSettings( + asConfigured(this.monsterSpawnLightTest, "monsterSpawnLightTest"), + this.monsterSpawnBlockLightLimit() + ), + this.skybox, + this.cardinalLightType, + this.attributes, + this.timelines, + this.defaultClock + ); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/PaperLevelStemRegistryEntry.java b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperLevelStemRegistryEntry.java new file mode 100644 index 000000000000..f10eab24ea4f --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/PaperLevelStemRegistryEntry.java @@ -0,0 +1,72 @@ +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.TypedKey; +import io.papermc.paper.registry.data.util.Conversions; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderGetter; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.presets.WorldPreset; +import org.jspecify.annotations.Nullable; + +import static io.papermc.paper.registry.data.util.Checks.asArgument; +import static io.papermc.paper.registry.data.util.Checks.asConfigured; + +public class PaperLevelStemRegistryEntry implements LevelStemRegistryEntry { + + protected @Nullable Holder type; + protected @Nullable ChunkGenerator generator; + + protected final Conversions conversions; + + public PaperLevelStemRegistryEntry(final Conversions conversions, final @Nullable LevelStem internal) { + this.conversions = conversions; + if (internal == null) { + return; + } + this.type = internal.type(); + this.generator = internal.generator(); + } + + @Override + public TypedKey type() { + return PaperRegistries.fromNms(asConfigured(this.type, "type").unwrapKey().orElseThrow()); + } + + public static final class PaperBuilder extends PaperLevelStemRegistryEntry implements LevelStemRegistryEntry.Builder, PaperRegistryBuilder { + + private final HolderGetter dimensionTypeHolderGetter; + private final HolderGetter worldPresetHolderGetter; + + public PaperBuilder(final Conversions conversions, final @Nullable LevelStem internal) { + super(conversions, internal); + this.dimensionTypeHolderGetter = conversions.lookup().lookup(Registries.DIMENSION_TYPE).orElseThrow().getter(); + this.worldPresetHolderGetter = conversions.lookup().lookup(Registries.WORLD_PRESET).orElseThrow().getter(); + } + + @Override + public Builder type(final TypedKey type) { + this.type = this.dimensionTypeHolderGetter.getOrThrow(PaperRegistries.toNms(asArgument(type, "type"))); + return this; + } + + @Override + public Builder copyGeneratorFrom(final TypedKey preset, final TypedKey levelStem) { + final Holder presetHolder = this.worldPresetHolderGetter.getOrThrow(PaperRegistries.toNms(asArgument(preset, "preset"))); + final LevelStem targetStem = presetHolder.value().dimensions.get(PaperRegistries.toNms(asArgument(levelStem, "levelStem"))); + Preconditions.checkArgument(targetStem != null, "Level stem %s not found in preset %s", levelStem, preset); + this.generator = this.conversions.clone(targetStem.generator(), ChunkGenerator.CODEC); + return this; + } + + @Override + public LevelStem build() { + return new LevelStem(asConfigured(this.type, "type"), asConfigured(this.generator, "generator")); + } + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/registry/data/util/Conversions.java b/paper-server/src/main/java/io/papermc/paper/registry/data/util/Conversions.java index 4625d82e6514..ad6ec3b15196 100644 --- a/paper-server/src/main/java/io/papermc/paper/registry/data/util/Conversions.java +++ b/paper-server/src/main/java/io/papermc/paper/registry/data/util/Conversions.java @@ -99,4 +99,8 @@ private > PaperRegistry return new PaperRegistryBuilderFactory<>(resourceRegistryKey, this, buildableMeta.builderFiller(), lookupForBuilders::getValueForCopying); } + public T clone(final T obj, final Codec directCodec) { + final Object serialized = directCodec.encodeStart(this.javaOps, obj).getOrThrow(IllegalArgumentException::new); + return directCodec.parse(this.javaOps, serialized).getOrThrow(IllegalArgumentException::new); + } } diff --git a/paper-server/src/main/java/io/papermc/paper/world/PaperWorldPreset.java b/paper-server/src/main/java/io/papermc/paper/world/PaperWorldPreset.java new file mode 100644 index 000000000000..0276cba232c3 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/PaperWorldPreset.java @@ -0,0 +1,23 @@ +package io.papermc.paper.world; + +import io.papermc.paper.registry.HolderableBase; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import org.bukkit.craftbukkit.CraftRegistry; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public final class PaperWorldPreset extends HolderableBase implements WorldPreset { + + public PaperWorldPreset(final Holder holder) { + super(holder); + } + + public static WorldPreset minecraftHolderToBukkit(final Holder minecraft) { + return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.WORLD_PRESET); + } + + public static Holder bukkitToMinecraftHolder(final WorldPreset bukkit) { + return CraftRegistry.bukkitToMinecraftHolder(bukkit); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java b/paper-server/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java index f1b9de16b801..089508c08119 100644 --- a/paper-server/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java +++ b/paper-server/src/main/java/io/papermc/paper/world/worldgen/OptionallyFlatBedrockConditionSource.java @@ -6,8 +6,8 @@ import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.level.levelgen.PositionalRandomFactory; diff --git a/paper-server/src/main/java/io/papermc/paper/world/worldgen/PaperDimensionType.java b/paper-server/src/main/java/io/papermc/paper/world/worldgen/PaperDimensionType.java new file mode 100644 index 000000000000..bcd863429736 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/worldgen/PaperDimensionType.java @@ -0,0 +1,23 @@ +package io.papermc.paper.world.worldgen; + +import io.papermc.paper.registry.HolderableBase; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import org.bukkit.craftbukkit.CraftRegistry; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class PaperDimensionType extends HolderableBase implements DimensionType { + + public PaperDimensionType(final Holder holder) { + super(holder); + } + + public static DimensionType minecraftHolderToBukkit(final Holder minecraft) { + return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.DIMENSION_TYPE); + } + + public static Holder bukkitToMinecraftHolder(final DimensionType bukkit) { + return CraftRegistry.bukkitToMinecraftHolder(bukkit); + } +} diff --git a/paper-server/src/main/java/io/papermc/paper/world/worldgen/PaperLevelStem.java b/paper-server/src/main/java/io/papermc/paper/world/worldgen/PaperLevelStem.java new file mode 100644 index 000000000000..30e9c047e6a6 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/world/worldgen/PaperLevelStem.java @@ -0,0 +1,28 @@ +package io.papermc.paper.world.worldgen; + +import io.papermc.paper.registry.HolderableBase; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import org.bukkit.craftbukkit.CraftRegistry; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class PaperLevelStem extends HolderableBase implements LevelStem { + + public PaperLevelStem(final Holder holder) { + super(holder); + } + + public static LevelStem minecraftHolderToBukkit(final Holder minecraft) { + return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.LEVEL_STEM); + } + + public static Holder bukkitToMinecraftHolder(final LevelStem bukkit) { + return CraftRegistry.bukkitToMinecraftHolder(bukkit); + } + + @Override + public DimensionType getDimensionType() { + return PaperDimensionType.minecraftHolderToBukkit(this.getHandle().type()); + } +} diff --git a/paper-server/src/main/resources/META-INF/services/io.papermc.paper.math.provider.IntProviderProvider b/paper-server/src/main/resources/META-INF/services/io.papermc.paper.math.provider.IntProviderProvider new file mode 100644 index 000000000000..f6f316fcf505 --- /dev/null +++ b/paper-server/src/main/resources/META-INF/services/io.papermc.paper.math.provider.IntProviderProvider @@ -0,0 +1 @@ +io.papermc.paper.math.provider.PaperIntProviderProvider diff --git a/paper-server/src/test/java/io/papermc/paper/world/flag/FeatureFlagTest.java b/paper-server/src/test/java/io/papermc/paper/world/flag/FeatureFlagTest.java index df2f4919eb89..55d60fede2f5 100644 --- a/paper-server/src/test/java/io/papermc/paper/world/flag/FeatureFlagTest.java +++ b/paper-server/src/test/java/io/papermc/paper/world/flag/FeatureFlagTest.java @@ -83,8 +83,8 @@ void testApiImplementsFeatureDependant(final RegistryKey re static Stream> nonFeatureFilteredRegistries() { return RegistryHelper.registryAccess().registries().filter(r -> { final RegistryEntry entry = PaperRegistries.getEntry(r.key()); - // has an API registry and isn't a filtered registry - return entry != null && !FeatureElement.FILTERED_REGISTRIES.contains(r.key()); + // has an API registry and isn't a filtered registry (non empty) + return entry != null && !FeatureElement.FILTERED_REGISTRIES.contains(r.key()) && r.value().listElements().findAny().isPresent(); }).map(r -> PaperRegistries.getEntry(r.key()).apiKey()); } diff --git a/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java b/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java index c22227c4cf24..62812a420061 100644 --- a/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java +++ b/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java @@ -9,6 +9,14 @@ import io.papermc.paper.entity.poi.PoiTypes; import io.papermc.paper.registry.PaperRegistries; import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.world.PaperWorldPreset; +import io.papermc.paper.world.WorldPreset; +import io.papermc.paper.world.WorldPresets; +import io.papermc.paper.world.worldgen.DimensionType; +import io.papermc.paper.world.worldgen.DimensionTypes; +import io.papermc.paper.world.worldgen.LevelStem; +import io.papermc.paper.world.worldgen.PaperDimensionType; +import io.papermc.paper.world.worldgen.PaperLevelStem; import java.util.List; import java.util.stream.Stream; import net.minecraft.core.Registry; @@ -165,6 +173,9 @@ public Object[] get() { register(Registries.GAME_RULE, GameRule.class, GameRules.class, CraftGameRule.class, net.minecraft.world.level.gamerules.GameRule.class); register(Registries.POINT_OF_INTEREST_TYPE, PoiType.class, PoiTypes.class, PaperPoiType.class, net.minecraft.world.entity.ai.village.poi.PoiType.class); register(Registries.SULFUR_CUBE_ARCHETYPE, SulfurCube.Archetype.class, CraftSulfurCube.CraftArchetype.class, SulfurCubeArchetype.class); + register(Registries.WORLD_PRESET, WorldPreset.class, WorldPresets.class, PaperWorldPreset.class, net.minecraft.world.level.levelgen.presets.WorldPreset.class); + register(Registries.DIMENSION_TYPE, DimensionType.class, DimensionTypes.class, PaperDimensionType.class, net.minecraft.world.level.dimension.DimensionType.class); + register(Registries.LEVEL_STEM, LevelStem.class, PaperLevelStem.class, net.minecraft.world.level.dimension.LevelStem.class); } private static void register(ResourceKey> registryKey, Class api, Class impl, Class internal) { diff --git a/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java b/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java index fe2b287b257e..55863e4ae87b 100644 --- a/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java +++ b/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java @@ -2,6 +2,13 @@ import io.papermc.paper.plugin.bootstrap.BootstrapContext; import io.papermc.paper.plugin.bootstrap.PluginBootstrap; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.registry.event.RegistryEvents; +import io.papermc.paper.registry.keys.DimensionTypeKeys; +import io.papermc.paper.registry.keys.WorldPresetKeys; +import io.papermc.paper.world.worldgen.LevelStem; +import net.kyori.adventure.key.Key; import org.jetbrains.annotations.NotNull; public class TestPluginBootstrap implements PluginBootstrap { @@ -9,6 +16,14 @@ public class TestPluginBootstrap implements PluginBootstrap { @Override public void bootstrap(@NotNull BootstrapContext context) { // io.papermc.testplugin.brigtests.Registration.registerViaBootstrap(context); + + final TypedKey NEW = TypedKey.create(RegistryKey.LEVEL_STEM, Key.key("mm:test")); + context.getLifecycleManager().registerEventHandler(RegistryEvents.LEVEL_STEM.compose(), event -> { + event.registry().register(NEW, b -> { + b.type(DimensionTypeKeys.OVERWORLD) + .copyGeneratorFrom(WorldPresetKeys.AMPLIFIED, TypedKey.create(RegistryKey.LEVEL_STEM, Key.key("minecraft:the_nether"))); + }); + }); } }