Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"dimensions": {
"minecraft:overworld": {
"type": "minecraft:overworld",
"generator": {
"type": "limlib:debug_dynamic_chunk_generator"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
{
"id": "limlib:debug_nbt",
"required": false
},
{
"id": "limlib:debug_dynamic_nbt",
"required": false
}
]
}
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

public abstract class AbstractNbtChunkGenerator extends LiminalChunkGenerator {

public final NbtGroup nbtGroup;
public final FunctionMap<ResourceLocation, NbtPlacerUtil, ResourceManager> structures;
protected NbtGroup nbtGroup;
protected FunctionMap<ResourceLocation, NbtPlacerUtil, ResourceManager> structures;

public AbstractNbtChunkGenerator(BiomeSource biomeSource, NbtGroup nbtGroup) {
this(biomeSource, nbtGroup, new FunctionMap<ResourceLocation, NbtPlacerUtil, ResourceManager>(NbtPlacerUtil::load));
Expand All @@ -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<ResourceLocation, NbtPlacerUtil, ResourceManager> 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))
Expand All @@ -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))
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class MazeGenerator<M extends MazeComponent> {
/**
* Creates a rectangular maze generator.
* <p>
*
*
* @param width of the maze
* @param height of the maze
* @param thicknessX of the cells in real world coordinates.
Expand All @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
167 changes: 167 additions & 0 deletions src/main/java/net/ludocrypt/limlib/api/world/pool/POOL_GUIDE.md
Original file line number Diff line number Diff line change
@@ -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"
]
}
}
```
Loading