diff --git a/src/main/generated/.cache/670404fc38c38f10433c6d717c37d9914ac13e68 b/src/main/generated/.cache/670404fc38c38f10433c6d717c37d9914ac13e68 index 2084809..abd1a02 100644 --- a/src/main/generated/.cache/670404fc38c38f10433c6d717c37d9914ac13e68 +++ b/src/main/generated/.cache/670404fc38c38f10433c6d717c37d9914ac13e68 @@ -1,2 +1,3 @@ -// 1.21.1 2026-02-07T20:57:09.6940347 Liminal Library/Liminal Test +// 1.21.1 2026-06-25T02:08:28.4109926 Liminal Library/Liminal Test +3fb71931ad339015729a8052c2fe0d45c95d0b01 data\limlib\worldgen\world_preset\debug_dynamic_nbt.json c4d20d32c179a90e60333fb84b40a371767160b6 data\limlib\worldgen\world_preset\debug_nbt.json diff --git a/src/main/generated/.cache/ac273c87971f83c879b0c3ad613d3e6a2a1a14bf b/src/main/generated/.cache/ac273c87971f83c879b0c3ad613d3e6a2a1a14bf index ccb6409..f0a2265 100644 --- a/src/main/generated/.cache/ac273c87971f83c879b0c3ad613d3e6a2a1a14bf +++ b/src/main/generated/.cache/ac273c87971f83c879b0c3ad613d3e6a2a1a14bf @@ -1,2 +1,2 @@ -// 1.21.1 2026-02-07T20:57:09.6930366 Liminal Library/Tags for minecraft:worldgen/world_preset -624fcdfaa797001ceed40d0a1e985ecfeca8c150 data\minecraft\tags\worldgen\world_preset\extended.json +// 1.21.1 2026-06-25T02:08:28.4099944 Liminal Library/Tags for minecraft:worldgen/world_preset +5e03860ba448137bcf194f6d915d5af1fe89cba3 data\minecraft\tags\worldgen\world_preset\extended.json diff --git a/src/main/generated/data/limlib/worldgen/world_preset/debug_dynamic_nbt.json b/src/main/generated/data/limlib/worldgen/world_preset/debug_dynamic_nbt.json new file mode 100644 index 0000000..51e92b5 --- /dev/null +++ b/src/main/generated/data/limlib/worldgen/world_preset/debug_dynamic_nbt.json @@ -0,0 +1,10 @@ +{ + "dimensions": { + "minecraft:overworld": { + "type": "minecraft:overworld", + "generator": { + "type": "limlib:debug_dynamic_chunk_generator" + } + } + } +} \ No newline at end of file diff --git a/src/main/generated/data/minecraft/tags/worldgen/world_preset/extended.json b/src/main/generated/data/minecraft/tags/worldgen/world_preset/extended.json index d224942..de70bd0 100644 --- a/src/main/generated/data/minecraft/tags/worldgen/world_preset/extended.json +++ b/src/main/generated/data/minecraft/tags/worldgen/world_preset/extended.json @@ -3,6 +3,10 @@ { "id": "limlib:debug_nbt", "required": false + }, + { + "id": "limlib:debug_dynamic_nbt", + "required": false } ] } \ No newline at end of file diff --git a/src/main/java/net/ludocrypt/limlib/api/world/chunk/AbstractDynamicChunkGenerator.java b/src/main/java/net/ludocrypt/limlib/api/world/chunk/AbstractDynamicChunkGenerator.java new file mode 100644 index 0000000..e56c8e4 --- /dev/null +++ b/src/main/java/net/ludocrypt/limlib/api/world/chunk/AbstractDynamicChunkGenerator.java @@ -0,0 +1,33 @@ +package net.ludocrypt.limlib.api.world.chunk; + +import net.ludocrypt.limlib.api.world.NbtGroup; +import net.minecraft.world.level.biome.BiomeSource; + +public abstract class AbstractDynamicChunkGenerator extends AbstractNbtChunkGenerator implements DynamicNbtUpdater { + public AbstractDynamicChunkGenerator(BiomeSource biomeSource, NbtGroup defaultNbtGroup) { + super(biomeSource, defaultNbtGroup); + } + + /** + * Use this method to get your NbtGroup for generating and placing NBTs down + * @return Your dynamically-referenced {@link NbtGroup}, + * or your default one set via the {@link AbstractDynamicChunkGenerator#AbstractDynamicChunkGenerator(BiomeSource, NbtGroup) initializer} + */ + @Override + public NbtGroup getGroup() { + try { + return getDynamicGroup(); + } catch (Exception e) { + return getStandardNbtGroup(); + } + } + + /** + * Responsible for pulling a dynamically-referenced {@link NbtGroup}. + * This means that, rather than changing data within the group, + * you're changing the group reference itself based on conditionals and where you retrieve it from. + * @return a dynamically-referenced {@link NbtGroup} + * @see net.ludocrypt.limlib.impl.debug.DebugDynamicChunkGenerator DebugDynamicChunkGenerator for an example implementation + */ + public abstract NbtGroup getDynamicGroup(); +} diff --git a/src/main/java/net/ludocrypt/limlib/api/world/chunk/AbstractNbtChunkGenerator.java b/src/main/java/net/ludocrypt/limlib/api/world/chunk/AbstractNbtChunkGenerator.java index 9410460..5a9ecf7 100644 --- a/src/main/java/net/ludocrypt/limlib/api/world/chunk/AbstractNbtChunkGenerator.java +++ b/src/main/java/net/ludocrypt/limlib/api/world/chunk/AbstractNbtChunkGenerator.java @@ -24,8 +24,8 @@ public abstract class AbstractNbtChunkGenerator extends LiminalChunkGenerator { - public final NbtGroup nbtGroup; - public final FunctionMap structures; + protected NbtGroup nbtGroup; + protected FunctionMap structures; public AbstractNbtChunkGenerator(BiomeSource biomeSource, NbtGroup nbtGroup) { this(biomeSource, nbtGroup, new FunctionMap(NbtPlacerUtil::load)); @@ -39,14 +39,28 @@ public AbstractNbtChunkGenerator(BiomeSource biomeSource, NbtGroup nbtGroup, this.nbtGroup.fill(structures); } + /** + * Only use this for nbt generation if you aren't extending from {@link AbstractDynamicChunkGenerator}, + * and aren't implementing {@link DynamicNbtUpdater} + * @return The standard {@link NbtGroup} object passed into the initializer + * @see DynamicNbtUpdater#getGroup() + * @see AbstractDynamicChunkGenerator#getDynamicGroup() + */ + public NbtGroup getStandardNbtGroup() { + return this.nbtGroup; + } + + public FunctionMap getStructures() { + return this.structures; + } + public void generateNbt(WorldGenRegion region, BlockPos at, ResourceLocation id) { generateNbt(region, at, id, Manipulation.NONE); } public void generateNbt(WorldGenRegion region, BlockPos at, ResourceLocation id, Manipulation manipulation) { - try { - structures + getStructures() .eval(id, region.getServer().getResourceManager()) .manipulate(manipulation) .generateNbt(region, at, (pos, state, nbt) -> this.modifyStructure(region, pos, state, nbt)) @@ -64,9 +78,8 @@ public void generateNbt(WorldGenRegion region, BlockPos offset, BlockPos from, B public void generateNbt(WorldGenRegion region, BlockPos offset, BlockPos from, BlockPos to, ResourceLocation id, Manipulation manipulation) { - try { - structures + getStructures() .eval(id, region.getServer().getResourceManager()) .manipulate(manipulation) .generateNbt(region, offset, from, to, (pos, state, nbt) -> this.modifyStructure(region, pos, state, nbt)) diff --git a/src/main/java/net/ludocrypt/limlib/api/world/chunk/DynamicNbtUpdater.java b/src/main/java/net/ludocrypt/limlib/api/world/chunk/DynamicNbtUpdater.java new file mode 100644 index 0000000..b4887c1 --- /dev/null +++ b/src/main/java/net/ludocrypt/limlib/api/world/chunk/DynamicNbtUpdater.java @@ -0,0 +1,25 @@ +package net.ludocrypt.limlib.api.world.chunk; + +import net.ludocrypt.limlib.api.world.FunctionMap; +import net.ludocrypt.limlib.api.world.NbtGroup; +import net.ludocrypt.limlib.api.world.NbtPlacerUtil; + +public interface DynamicNbtUpdater { + /** + * Tells the updater how to get the NbtGroup for dynamically updating. + * You are advised to use {@link AbstractDynamicChunkGenerator} whenever possible, + * as it handles most of the boilerplate for this automatically + * @return the {@link NbtGroup} intended for nbt generation + * @see AbstractDynamicChunkGenerator + * @see AbstractNbtChunkGenerator#getStandardNbtGroup() + */ + NbtGroup getGroup(); + + default void update(AbstractNbtChunkGenerator chunkGenerator) { + if (chunkGenerator.nbtGroup != getGroup()) { + chunkGenerator.nbtGroup = getGroup(); + chunkGenerator.structures = new FunctionMap<>(NbtPlacerUtil::load); + chunkGenerator.nbtGroup.fill(chunkGenerator.structures); + } + } +} diff --git a/src/main/java/net/ludocrypt/limlib/api/world/maze/MazeGenerator.java b/src/main/java/net/ludocrypt/limlib/api/world/maze/MazeGenerator.java index 0f4e025..3ae9a91 100644 --- a/src/main/java/net/ludocrypt/limlib/api/world/maze/MazeGenerator.java +++ b/src/main/java/net/ludocrypt/limlib/api/world/maze/MazeGenerator.java @@ -20,7 +20,7 @@ public class MazeGenerator { /** * Creates a rectangular maze generator. *

- * + * * @param width of the maze * @param height of the maze * @param thicknessX of the cells in real world coordinates. @@ -39,9 +39,8 @@ public MazeGenerator(int width, int height, int thicknessX, int thicknessY, long /** * Begins generating a maze starting at pos. This should be run in every chunk * with the pos being the beginning position of the chunk. - * + * * @param pos the starting position for the maze logic to work. - * @param seed the world seed * @param mazeCreator functional interface to create a new maze at a position * @param cellDecorator funcional interface to generate a single maze block, or * 'cell' diff --git a/src/main/java/net/ludocrypt/limlib/api/world/pool/LimlibPoolApi.java b/src/main/java/net/ludocrypt/limlib/api/world/pool/LimlibPoolApi.java new file mode 100644 index 0000000..f33d95c --- /dev/null +++ b/src/main/java/net/ludocrypt/limlib/api/world/pool/LimlibPoolApi.java @@ -0,0 +1,18 @@ +package net.ludocrypt.limlib.api.world.pool; + +import net.ludocrypt.limlib.api.world.NbtGroup; +import net.minecraft.resources.ResourceLocation; + +public class LimlibPoolApi { + public static PiecePool getPool(ResourceLocation id) { + return PoolStorage.getPool(id); + } + + public static NbtGroup getPoolAsGroup(ResourceLocation id) { + return getPool(id).convertToGroup(); + } + + public static void initialize() { + PoolStorage.initializePoolStorage(); + } +} diff --git a/src/main/java/net/ludocrypt/limlib/api/world/pool/POOL_GUIDE.md b/src/main/java/net/ludocrypt/limlib/api/world/pool/POOL_GUIDE.md new file mode 100644 index 0000000..e5bdfdf --- /dev/null +++ b/src/main/java/net/ludocrypt/limlib/api/world/pool/POOL_GUIDE.md @@ -0,0 +1,167 @@ +# Guide to Piece Pools +> AKA Making your NBT-based dimensions more datapack-modifiable +## Introduction +When making chunk generators that use NbtGroups, you may realize that adding more nbt pieces to your chunk generator's +Nbt-Group requires manually editing your NbtGroup variable. +This makes it near-impossible for 3rd-party addon and modpack developers +to expand your dimension and add variety to it, without very risky mixin or reflection usage. + +### The Solution? We create a datapack-definable and datapack-modifiable object that gets converted into a Nbt-Group +This is the purpose of the Piece-Pool system: to create a dynamically-referencable object that can be modified via +datapacks, and converted into a Nbt-Group on-command. + +# For Mod Developers +## Preparation +The first step is to prepare our chunk generator to use a Piece Pool. + +This is done by making it either extend `AbstractDynamicChunkGenerator`, or implement the `DynamicNbtUpdater` interface. +You are highly recommended to extend the former as it handles errors by returning the initializer-passed NbtGroup as a +fail-safe; for the sake of this guide, we will assume you're extending `AbstractDynamicChunkGenerator` + +You may use `DynamicNbtUpdater` ***ONLY** when you feel the need to use your own default-NbtGroup and/or error handling.* + +### AbstractDynamicChunkGenerator +When extending `AbstractDynamicChunkGenerator`, you will need to pass a default NbtGroup into your super constructor; +this is done to assure that, even when the Piece Pool cannot be found, there is still an NbtGroup to be used for generation. + +Next, we override `getDynamicGroup`; this will be the method we use to tell the chunk generator how to get our dynamic +Nbt-Group, that will be prioritized over our default Nbt-Group. + +Once you have done these steps, your chunk generator will be ready for Piece Pool usage. + +### Note: to actually use your nbt-group, simply use `this.nbtgroup` like you normally would; it is updated automatically +the `DynamicNbtUpdater` interface to be the Nbt-Group referenced in `getDynamicGroup` + +Example: + +```java +import net.ludocrypt.limlib.api.world.NbtGroup; +import net.ludocrypt.limlib.api.world.chunk.AbstractDynamicChunkGenerator; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.BiomeSource; + +public class ExampleDynamicChunkGenerator extends AbstractDynamicChunkGenerator { + public static final ResourceLocation POOL_ID = ResourceLocation.fromNamespaceAndPath("example_mod", "example"); + + // Declare and initialize our default nbt-group + public static final NbtGroup DEFAULT_NBTGROUP = NbtGroup.Builder + .create(POOL_ID) + .with("set_1", "a", "b") + .with("set_2", "c", "d") + .build(); + + public ExampleDynamicChunkGenerator(BiomeSource source) { + // Pass our default nbt-group into the super constructor + super(source, DEFAULT_NBTGROUP); + } + + @Override + public NbtGroup getDynamicGroup() { + //TODO + } +} +``` + +## Using Piece Pools +Now that your chunk generator is ready, we can make use of Piece Pools + +A piece-pool is an object containing the following data: +- `pool`, which corresponds to the NbtGroup's identifier and the location of its nbt files in `modid:structures/nbt/[pool_name]` +- `sub_pools`, a map where each entry has the name of the sub-pool as the key, and the value as a list of strings, +corresponding to the nbt names +- `override`, optional and intended for modpack-creators, this allows for your sub-pool to replace all entries in the nbt group with +your own. + +To create a piece-pool, we add a JSON file to `modid:worldgen/piece_pools`; it can be any name, as it is the internal +`pool` value that's used as the name of the piece-pool and not the file-name itself. + +Next, we call the `LimlibPoolApi` in `getDynamicGroup`, to retrieve our Piece Pool and convert it to a Nbt Group. +Whenever the piece-pool is detected to have changed (from datapack-reloading, for example), the chunk generator will +update its `nbtgroup` variable accordingly. + +`LimlibPoolApi#getPoolAsGroup()` is the intended method to handle retrieval-and-conversion of piece pools in your code, +but you may choose to call `LimlibPoolApi#getPool()` for if you want to make some pre-conversion changes in-code instead; +simply call `PiecePool#convertToGroup()` once you're done. + +Example (JSON), with additional pieces added +```json +{ + "pool": "example_mod:example", + "sub_pools": { + "set_1": [ + "a", + "b", + "c", + "d" + ], + "set_2": [ + "c", + "d", + "e", + "f" + ] + } +} +``` + +Example (Code) +```java +import net.ludocrypt.limlib.api.world.NbtGroup; +import net.ludocrypt.limlib.api.world.chunk.AbstractDynamicChunkGenerator; +import net.ludocrypt.limlib.api.world.pool.LimlibPoolApi; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.BiomeSource; + +public class ExampleDynamicChunkGenerator extends AbstractDynamicChunkGenerator { + public static final ResourceLocation POOL_ID = ResourceLocation.fromNamespaceAndPath("example_mod", "example"); + + // Declare and initialize our default nbt-group + public static final NbtGroup DEFAULT_NBTGROUP = NbtGroup.Builder + .create(POOL_ID) + .with("set_1", "a", "b") + .with("set_2", "c", "d") + .build(); + + public ExampleDynamicChunkGenerator(BiomeSource source) { + // Pass our default nbt-group into the super constructor + super(source, DEFAULT_NBTGROUP); + } + + @Override + public NbtGroup getDynamicGroup() { + return LimlibPoolApi.getPoolAsGroup(POOL_ID); + } +} +``` + +Congratulations, your chunk generator is now datapack-modifiable! Now, addon-developers and modpack-developers can +add their own nbts to the generator by creating a Piece Pool JSON in their datapack, and targeting your generator's +piece pool by using the same identifier in the `pool` entry. + +### For add-on, and modpack developers +To add nbts into a dimension's chunk generator, it must be configured to use the Piece Pool system; incompatible generators +are not affected by the Piece Pool system, and as-such cannot be modified using it. + +A piece-pool is an object containing the following data: +- `pool`, which corresponds to the pool's identifier and the location of its nbt files in `modid:structures/nbt/[pool_name]` +- `sub_pools`, a map where each entry has the name of the sub-pool as the key, and the value as a list of strings, + corresponding to the nbt names +- `override`, optional and intended for modpack-creators, this allows for your pool to replace all entries in the nbt group with + your own. + +If we know the `pool` identifier used by the chunk generator, we can create a JSON representing our additions to the +Piece Pool using it + +Example of an addon-modification; this would add nbts "addon_a" and "addon_b" to the "set_1" sub-pool of +example mod's "example" pool +```json + { + "pool": "example_mod:example", + "sub_pools": { + "set_1": [ + "addon_a", + "addon_b" + ] + } +} +``` diff --git a/src/main/java/net/ludocrypt/limlib/api/world/pool/PiecePool.java b/src/main/java/net/ludocrypt/limlib/api/world/pool/PiecePool.java new file mode 100644 index 0000000..1409c06 --- /dev/null +++ b/src/main/java/net/ludocrypt/limlib/api/world/pool/PiecePool.java @@ -0,0 +1,83 @@ +package net.ludocrypt.limlib.api.world.pool; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.ludocrypt.limlib.api.world.NbtGroup; +import net.minecraft.resources.ResourceLocation; + +import java.util.*; + +public class PiecePool { + private final ResourceLocation pool; + protected final HashMap> subPools; + public final boolean shouldOverride; + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> + instance.group( + ResourceLocation.CODEC.fieldOf("pool").stable().forGetter(PiecePool::getPool), + Codec.unboundedMap(Codec.STRING, Codec.list(Codec.STRING)).fieldOf("sub_pools").forGetter(PiecePool::getSubPools), + Codec.BOOL.optionalFieldOf("override", false) + .stable().forGetter(pool -> pool.shouldOverride) + ).apply(instance, PiecePool::new)); + + public PiecePool(ResourceLocation pool, Map> subPools, boolean shouldOverride) { + this.pool = pool; + this.subPools = new HashMap<>(subPools); + this.shouldOverride = shouldOverride; + } + + public PiecePool(ResourceLocation pool, Map> subPools) { + this(pool, subPools, false); + } + + public ResourceLocation getPool() { + return pool; + } + + public Map> getSubPools() { + return Map.copyOf(subPools); + } + + public boolean hasSubPool(String subPool) { + return subPools.containsKey(subPool); + } + + public void addSubPool(String subPool, String... pieces) { + if (!hasSubPool(subPool)) { + subPools.put(subPool, List.of(pieces)); + } + } + + public void addPiecesToSubPool(String subPool, String... pieces) { + if (hasSubPool(subPool)) { + List filteredList = Arrays.stream(pieces) + .filter(piece -> !subPools.get(subPool).contains(piece)) + .toList(); + subPools.get(subPool).addAll(filteredList); + } + } + + public void removeSubPool(String subPool) { + subPools.remove(subPool); + } + + public void removePiecesFromSubPool(String subPool, String... pieces) { + if (hasSubPool(subPool)) { + List filteredList = Arrays.stream(pieces) + .filter(piece -> subPools.get(subPool).contains(piece)) + .toList(); + subPools.get(subPool).removeAll(filteredList); + } + } + + public NbtGroup convertToGroup() { + NbtGroup.Builder builder = NbtGroup.Builder + .create(pool); + + for (String subPool : subPools.keySet()) { + builder = builder.with(subPool, subPools.get(subPool).toArray(String[]::new)); + } + + return builder.build(); + } +} diff --git a/src/main/java/net/ludocrypt/limlib/api/world/pool/PoolStorage.java b/src/main/java/net/ludocrypt/limlib/api/world/pool/PoolStorage.java new file mode 100644 index 0000000..22575a0 --- /dev/null +++ b/src/main/java/net/ludocrypt/limlib/api/world/pool/PoolStorage.java @@ -0,0 +1,79 @@ +package net.ludocrypt.limlib.api.world.pool; + +import com.google.gson.JsonObject; +import com.mojang.serialization.JsonOps; +import net.fabricmc.fabric.api.resource.ResourceManagerHelper; +import net.fabricmc.fabric.api.resource.SimpleResourceReloadListener; +import net.ludocrypt.limlib.impl.Limlib; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.profiling.ProfilerFiller; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +public class PoolStorage { + private static Map POOLS = new HashMap<>(); + + private static void loadPoolData(Map newPoolMap) { + POOLS = newPoolMap; + } + + public static PiecePool getPool(ResourceLocation id) throws NoSuchElementException { + if (!POOLS.containsKey(id)) Limlib.LOGGER.error("This pool does not exist: {}", id, new NoSuchElementException()); + return POOLS.get(id); + } + + private static class PoolListener implements SimpleResourceReloadListener> { + @Override + public CompletableFuture> load(ResourceManager manager, ProfilerFiller profiler, Executor executor) { + return CompletableFuture.supplyAsync(() -> { + HashMap map = new HashMap<>(); + + for (var resource : manager.listResources("worldgen/piece_pools", id + -> id.getPath().endsWith(".json")).entrySet()) { + try (var inputStream = resource.getValue().open()) { + var json = Limlib.GSON.fromJson(new InputStreamReader(inputStream), JsonObject.class); + + PiecePool pool = PiecePool.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow(); + if (map.containsKey(pool.getPool()) && !pool.shouldOverride) { + ResourceLocation id = pool.getPool(); + for (String subPool : pool.getSubPools().keySet()) { + if (map.get(id).hasSubPool(subPool)) { + map.get(id).addPiecesToSubPool(subPool, pool.getSubPools().get(subPool).toArray(String[]::new)); + } else { + map.get(id).addSubPool(subPool, pool.getSubPools().get(subPool).toArray(String[]::new)); + } + } + } else { + map.put(pool.getPool(), pool); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return Map.copyOf(map); + }, executor); + } + + @Override + public CompletableFuture apply(Map data, ResourceManager manager, ProfilerFiller profiler, Executor executor) { + return CompletableFuture.runAsync(() -> loadPoolData(data), executor); + } + + @Override + public ResourceLocation getFabricId() { + return Limlib.id("pool_listener"); + } + } + + public static void initializePoolStorage() { + ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new PoolListener()); + } +} diff --git a/src/main/java/net/ludocrypt/limlib/datagen/DataGenInitalizer.java b/src/main/java/net/ludocrypt/limlib/datagen/DataGenInitalizer.java index f0ebaba..196a73e 100644 --- a/src/main/java/net/ludocrypt/limlib/datagen/DataGenInitalizer.java +++ b/src/main/java/net/ludocrypt/limlib/datagen/DataGenInitalizer.java @@ -14,6 +14,7 @@ import net.ludocrypt.limlib.api.skybox.Skybox; import net.ludocrypt.limlib.api.skybox.TexturedSkybox; import net.ludocrypt.limlib.impl.Limlib; +import net.ludocrypt.limlib.impl.debug.DebugDynamicChunkGenerator; import net.ludocrypt.limlib.impl.debug.DebugNbtChunkGenerator; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; @@ -37,6 +38,7 @@ public class DataGenInitalizer implements DataGeneratorEntrypoint { public static final ResourceKey DEBUG_KEY = ResourceKey.create(Registries.WORLD_PRESET, ResourceLocation.fromNamespaceAndPath("limlib", "debug_nbt")); + public static final ResourceKey DEBUG_DYNAMIC_KEY = ResourceKey.create(Registries.WORLD_PRESET, ResourceLocation.fromNamespaceAndPath("limlib", "debug_dynamic_nbt")); @Override public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { @@ -52,6 +54,11 @@ protected void configure(HolderLookup.Provider registries, Entries entries) { dimension, new DebugNbtChunkGenerator(biome) )) )); + entries.add(DEBUG_DYNAMIC_KEY, new WorldPreset( + Map.of(LevelStem.OVERWORLD, new LevelStem( + dimension, new DebugDynamicChunkGenerator(biome) + )) + )); }); }); } @@ -68,7 +75,7 @@ public DataProvider create(FabricDataOutput output, CompletableFuture(output, Registries.WORLD_PRESET, registriesFuture) { @Override protected void addTags(HolderLookup.Provider wrapperLookup) { - this.tag(WorldPresetTags.EXTENDED).addOptional(DEBUG_KEY.location()); + this.tag(WorldPresetTags.EXTENDED).addOptional(DEBUG_KEY.location()).addOptional(DEBUG_DYNAMIC_KEY.location()); } }; } diff --git a/src/main/java/net/ludocrypt/limlib/impl/Limlib.java b/src/main/java/net/ludocrypt/limlib/impl/Limlib.java index d36039a..ddbd94f 100644 --- a/src/main/java/net/ludocrypt/limlib/impl/Limlib.java +++ b/src/main/java/net/ludocrypt/limlib/impl/Limlib.java @@ -1,5 +1,7 @@ package net.ludocrypt.limlib.impl; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.registry.DynamicRegistries; import net.ludocrypt.limlib.api.Utils; @@ -9,6 +11,8 @@ import net.ludocrypt.limlib.api.effects.sound.distortion.DistortionEffect; import net.ludocrypt.limlib.api.effects.sound.reverb.ReverbEffect; import net.ludocrypt.limlib.api.skybox.Skybox; +import net.ludocrypt.limlib.api.world.pool.LimlibPoolApi; +import net.ludocrypt.limlib.impl.debug.DebugDynamicChunkGenerator; import net.ludocrypt.limlib.impl.debug.DebugNbtChunkGenerator; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; @@ -18,6 +22,7 @@ public class Limlib implements ModInitializer { public static final Logger LOGGER = LoggerFactory.getLogger("Limlib"); + public static final Gson GSON = new GsonBuilder().create(); public static ResourceLocation id(String id) { return ResourceLocation.fromNamespaceAndPath("limlib", id); @@ -31,8 +36,10 @@ public void onInitialize() { PostEffect.init(); Skybox.init(); DynamicRegistries.registerSynced(SoundEffects.SOUND_EFFECTS_KEY, SoundEffects.CODEC); + LimlibPoolApi.initialize(); Utils.register(BuiltInRegistries.CHUNK_GENERATOR, "debug_nbt_chunk_generator", DebugNbtChunkGenerator.CODEC); + Utils.register(BuiltInRegistries.CHUNK_GENERATOR, "debug_dynamic_chunk_generator", DebugDynamicChunkGenerator.CODEC); } } diff --git a/src/main/java/net/ludocrypt/limlib/impl/debug/DebugDynamicChunkGenerator.java b/src/main/java/net/ludocrypt/limlib/impl/debug/DebugDynamicChunkGenerator.java new file mode 100644 index 0000000..920255d --- /dev/null +++ b/src/main/java/net/ludocrypt/limlib/impl/debug/DebugDynamicChunkGenerator.java @@ -0,0 +1,101 @@ +package net.ludocrypt.limlib.impl.debug; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.ludocrypt.limlib.api.world.NbtGroup; +import net.ludocrypt.limlib.api.world.chunk.AbstractDynamicChunkGenerator; +import net.ludocrypt.limlib.api.world.pool.LimlibPoolApi; +import net.ludocrypt.limlib.impl.Limlib; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.resources.RegistryOps; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.biome.FixedBiomeSource; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class DebugDynamicChunkGenerator extends AbstractDynamicChunkGenerator { + public static final NbtGroup DEFAULT = NbtGroup.Builder.create(Limlib.id("debug_dynamic")) + .with("stone", "default_stone") + .with("nether", "default_nether") + .with("end", "default_end") + .build(); + + public static final MapCodec CODEC = RecordCodecBuilder + .mapCodec(instance -> instance + .group(RegistryOps.retrieveElement(Biomes.THE_VOID)) + .apply(instance, instance.stable(DebugDynamicChunkGenerator::new))); + + public DebugDynamicChunkGenerator(Holder.Reference reference) { + super(new FixedBiomeSource(reference), DEFAULT); + } + + @Override + public NbtGroup getDynamicGroup() { + return LimlibPoolApi.getPoolAsGroup(Limlib.id("debug_dynamic")); + } + + @Override + public int getPlacementRadius() { + return 0; + } + + @Override + public CompletableFuture populateNoise(WorldGenRegion chunkRegion, ServerLevel level, ChunkGenerator generator, ChunkAccess chunk, Blender blender, RandomState randomState, StructureManager structureManager) { + ChunkPos pos = chunk.getPos(); + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + chunkRegion.setBlock(pos.getWorldPosition().offset(x, 3, y), Blocks.WHITE_CONCRETE.defaultBlockState(), Block.UPDATE_KNOWN_SHAPE); + + chunkRegion.setBlock(pos.getWorldPosition().offset(x, -16, y), Blocks.BARRIER.defaultBlockState(), Block.UPDATE_KNOWN_SHAPE); + } + } + + if (chunk.getPos().getWorldPosition().getX() == 0 && chunk.getPos().getWorldPosition().getZ() == 0) { + return CompletableFuture.completedFuture(chunk); + } + + RandomSource source = RandomSource.create(chunkRegion.getSeed() + pos.x + pos.z); + BlockPos structurePos = pos.getWorldPosition().offset(6, 4, 6); + int randInt = source.nextInt(3); + + if (randInt == 0) { + generateNbt(chunkRegion, structurePos, this.nbtGroup.pick("stone", source)); + } else if (randInt == 1) { + generateNbt(chunkRegion, structurePos, this.nbtGroup.pick("nether", source)); + } else if (randInt == 2){ + generateNbt(chunkRegion, structurePos, this.nbtGroup.pick("end", source)); + } + + return CompletableFuture.completedFuture(chunk); + } + + @Override + protected @NotNull MapCodec codec() { + return CODEC; + } + + @Override + public int getGenDepth() { + return 448; + } + + @Override + public void addDebugScreenInfo(List list, RandomState randomState, BlockPos blockPos) { + + } +} diff --git a/src/main/java/net/ludocrypt/limlib/impl/mixin/ChunkStatusMixin.java b/src/main/java/net/ludocrypt/limlib/impl/mixin/ChunkStatusMixin.java index 70f9241..70d4824 100644 --- a/src/main/java/net/ludocrypt/limlib/impl/mixin/ChunkStatusMixin.java +++ b/src/main/java/net/ludocrypt/limlib/impl/mixin/ChunkStatusMixin.java @@ -5,6 +5,8 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; +import net.ludocrypt.limlib.api.world.chunk.AbstractNbtChunkGenerator; +import net.ludocrypt.limlib.api.world.chunk.DynamicNbtUpdater; import net.minecraft.server.level.*; import net.minecraft.util.StaticCache2D; import net.minecraft.world.level.StructureManager; @@ -29,7 +31,13 @@ public class ChunkStatusMixin { @WrapOperation(method = "generateNoise", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/ChunkGenerator;fillFromNoise(Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/levelgen/RandomState;Lnet/minecraft/world/level/StructureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)Ljava/util/concurrent/CompletableFuture;")) private static CompletableFuture limlib$liminalChunkGenerator1(ChunkGenerator instance, Blender blender, RandomState randomState, StructureManager structureManager, ChunkAccess chunkAccess, Operation> original, @Local WorldGenRegion worldGenRegion, @Local ServerLevel serverLevel) { - if(instance instanceof LiminalChunkGenerator liminalChunkGenerator) { + if (instance instanceof LiminalChunkGenerator liminalChunkGenerator) { + if (liminalChunkGenerator instanceof AbstractNbtChunkGenerator nbtChunkGenerator) { + if (nbtChunkGenerator instanceof DynamicNbtUpdater updater) { + updater.update(nbtChunkGenerator); + } + } + return liminalChunkGenerator.populateNoise(worldGenRegion, serverLevel, instance, chunkAccess, blender, randomState, structureManager); } diff --git a/src/main/resources/assets/limlib/lang/en_us.json b/src/main/resources/assets/limlib/lang/en_us.json index d18628e..eb3ef5a 100644 --- a/src/main/resources/assets/limlib/lang/en_us.json +++ b/src/main/resources/assets/limlib/lang/en_us.json @@ -1,3 +1,4 @@ { - "generator.limlib.debug_nbt": "Nbt Debug" + "generator.limlib.debug_nbt": "Nbt Debug", + "generator.limlib.debug_dynamic_nbt": "Nbt Dynamic Data" } diff --git a/src/main/resources/data/limlib/structures/nbt/debug_dynamic/end/data_end.nbt b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/end/data_end.nbt new file mode 100644 index 0000000..decd135 Binary files /dev/null and b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/end/data_end.nbt differ diff --git a/src/main/resources/data/limlib/structures/nbt/debug_dynamic/end/default_end.nbt b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/end/default_end.nbt new file mode 100644 index 0000000..91f08e0 Binary files /dev/null and b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/end/default_end.nbt differ diff --git a/src/main/resources/data/limlib/structures/nbt/debug_dynamic/nether/data_nether.nbt b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/nether/data_nether.nbt new file mode 100644 index 0000000..7ea0ac0 Binary files /dev/null and b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/nether/data_nether.nbt differ diff --git a/src/main/resources/data/limlib/structures/nbt/debug_dynamic/nether/default_nether.nbt b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/nether/default_nether.nbt new file mode 100644 index 0000000..d71d6c7 Binary files /dev/null and b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/nether/default_nether.nbt differ diff --git a/src/main/resources/data/limlib/structures/nbt/debug_dynamic/stone/data_stone.nbt b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/stone/data_stone.nbt new file mode 100644 index 0000000..6980c32 Binary files /dev/null and b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/stone/data_stone.nbt differ diff --git a/src/main/resources/data/limlib/structures/nbt/debug_dynamic/stone/default_stone.nbt b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/stone/default_stone.nbt new file mode 100644 index 0000000..c73c161 Binary files /dev/null and b/src/main/resources/data/limlib/structures/nbt/debug_dynamic/stone/default_stone.nbt differ diff --git a/src/main/resources/data/limlib/worldgen/piece_pools/debug_dynamic.json b/src/main/resources/data/limlib/worldgen/piece_pools/debug_dynamic.json new file mode 100644 index 0000000..5d9fbe8 --- /dev/null +++ b/src/main/resources/data/limlib/worldgen/piece_pools/debug_dynamic.json @@ -0,0 +1,17 @@ +{ + "pool": "limlib:debug_dynamic", + "sub_pools": { + "stone": [ + "default_stone", + "data_stone" + ], + "nether": [ + "default_nether", + "data_nether" + ], + "end": [ + "default_end", + "data_end" + ] + } +}