[Feature] BCLStructureBuilder supports Jigsaw Structures

This commit is contained in:
Frank 2023-06-06 21:02:28 +02:00
parent b69259cb4b
commit eee0a8b9c8
6 changed files with 299 additions and 96 deletions

View file

@ -0,0 +1,119 @@
package org.betterx.bclib.api.v2.levelgen.structures;
import org.betterx.worlds.together.tag.v3.TagManager;
import com.mojang.serialization.Codec;
import net.minecraft.data.worldgen.BootstapContext;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.structure.TerrainAdjustment;
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType;
import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement;
import java.util.concurrent.ConcurrentLinkedQueue;
abstract class BCLBaseStructureBuilder<S extends Structure, T extends BCLBaseStructureBuilder<S, T>> {
static final ConcurrentLinkedQueue<BCLStructure.Unbound<?>> UNBOUND_STRUCTURES = new ConcurrentLinkedQueue<>();
static final ConcurrentLinkedQueue<BCLStructure.Unbound<?>> UNBOUND_STRUCTURE_SETS = new ConcurrentLinkedQueue<>();
protected final ResourceLocation structureID;
protected BCLStructure.StructureBuilder<S> structureBuilder;
private GenerationStep.Decoration step;
private StructurePlacement placement;
private TagKey<Biome> biomeTag;
private TerrainAdjustment terrainAdjustment;
protected BCLBaseStructureBuilder(
ResourceLocation structureID,
BCLStructure.StructureBuilder<S> structureBuilder
) {
this.structureID = structureID;
this.structureBuilder = structureBuilder;
this.step = GenerationStep.Decoration.SURFACE_STRUCTURES;
this.terrainAdjustment = TerrainAdjustment.NONE;
this.placement = null;
this.biomeTag = null;
}
public T adjustment(TerrainAdjustment value) {
this.terrainAdjustment = value;
return (T) this;
}
public T step(GenerationStep.Decoration value) {
this.step = value;
return (T) this;
}
public T placement(StructurePlacement value) {
this.placement = value;
return (T) this;
}
public T randomPlacement(int spacing, int separation) {
this.placement = new RandomSpreadStructurePlacement(
spacing,
separation,
RandomSpreadType.LINEAR,
13323129 + spacing + separation + structureID.toString().hashCode() % 10000
);
return (T) this;
}
public T biomeTag(String modID, String path) {
this.biomeTag = TagManager.BIOMES.makeStructureTag(modID, path);
return (T) this;
}
public T biomeTag(TagKey<Biome> tag) {
this.biomeTag = tag;
return (T) this;
}
protected abstract Codec<S> getCodec();
public BCLStructure<S> build() {
if (placement == null) {
throw new IllegalStateException("Placement needs to be defined for " + this.structureID);
}
if (structureBuilder == null) {
throw new IllegalStateException("A structure builder needs to be defined for " + this.structureID);
}
if (biomeTag == null) biomeTag(structureID.getNamespace(), structureID.getPath());
var res = new BCLStructure.Unbound<>(
structureID,
step,
placement,
getCodec(),
biomeTag,
structureBuilder,
terrainAdjustment
);
UNBOUND_STRUCTURES.add(res);
UNBOUND_STRUCTURE_SETS.add(res);
return res;
}
static void registerUnbound(BootstapContext<Structure> context) {
UNBOUND_STRUCTURES.forEach(s -> s.register(context));
UNBOUND_STRUCTURES.clear();
}
static void registerUnboundSets(BootstapContext<StructureSet> context) {
UNBOUND_STRUCTURE_SETS.forEach(s -> s.registerSet(context));
UNBOUND_STRUCTURE_SETS.clear();
}
}

View file

@ -0,0 +1,101 @@
package org.betterx.bclib.api.v2.levelgen.structures;
import com.mojang.serialization.Codec;
import net.minecraft.core.HolderGetter;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.VerticalAnchor;
import net.minecraft.world.level.levelgen.heightproviders.ConstantHeight;
import net.minecraft.world.level.levelgen.heightproviders.HeightProvider;
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
import net.minecraft.world.level.levelgen.structure.structures.JigsawStructure;
import java.util.Optional;
public class BCLJigsawStructureBuilder extends BCLBaseStructureBuilder<JigsawStructure, BCLJigsawStructureBuilder> {
private ResourceKey<StructureTemplatePool> startPool;
private Optional<ResourceLocation> startJigsawName;
private int maxDepth;
private HeightProvider startHeight;
private boolean useExpansionHack;
private Optional<Heightmap.Types> projectStartToHeightmap;
private int maxDistanceFromCenter;
public BCLJigsawStructureBuilder(
ResourceLocation structureID
) {
super(structureID, null);
this.maxDepth = 6;
this.startHeight = ConstantHeight.of(VerticalAnchor.absolute(0));
this.maxDistanceFromCenter = 80;
this.useExpansionHack = true;
this.startJigsawName = Optional.empty();
this.projectStartToHeightmap = Optional.empty();
}
public BCLJigsawStructureBuilder projectStartToHeightmap(Heightmap.Types value) {
this.projectStartToHeightmap = Optional.of(value);
return this;
}
public BCLJigsawStructureBuilder maxDistanceFromCenter(int value) {
this.maxDistanceFromCenter = value;
return this;
}
public BCLJigsawStructureBuilder startJigsawName(ResourceLocation value) {
this.startJigsawName = Optional.of(value);
return this;
}
public BCLJigsawStructureBuilder useExpansionHack(boolean value) {
this.useExpansionHack = value;
return this;
}
public BCLJigsawStructureBuilder maxDepth(int value) {
this.maxDepth = value;
return this;
}
public BCLJigsawStructureBuilder startHeight(HeightProvider value) {
this.startHeight = value;
return this;
}
public BCLJigsawStructureBuilder startPool(ResourceKey<StructureTemplatePool> pool) {
this.startPool = pool;
return this;
}
@Override
protected Codec<JigsawStructure> getCodec() {
return JigsawStructure.CODEC;
}
@Override
public BCLStructure<JigsawStructure> build() {
if (startPool == null) {
throw new IllegalStateException("Start pool must be set for " + this.structureID);
}
this.structureBuilder = (BCLStructure.StructureBuilderWithContext<JigsawStructure>) (structureSettings, ctx) -> {
HolderGetter<StructureTemplatePool> templateGetter = ctx.lookup(Registries.TEMPLATE_POOL);
return new JigsawStructure(
structureSettings,
templateGetter.getOrThrow(startPool),
startJigsawName,
maxDepth,
startHeight,
useExpansionHack,
projectStartToHeightmap,
maxDistanceFromCenter
);
};
return super.build();
}
}

View file

@ -20,12 +20,27 @@ import com.google.common.collect.Lists;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public abstract class BCLStructure<S extends Structure> { public abstract class BCLStructure<S extends Structure> {
public interface StructureBuilder<S extends Structure> {
S apply(Structure.StructureSettings structureSettings);
}
public interface StructureCodecProvider<S extends Structure> {
Codec<S> getCodec();
}
public interface StructureBuilderWithContext<S extends Structure> extends StructureBuilder<S> {
default S apply(Structure.StructureSettings structureSettings) {
return apply(structureSettings, null);
}
S apply(Structure.StructureSettings structureSettings, BootstapContext<Structure> ctx);
}
public static class Unbound<S extends Structure> extends BCLStructure<S> { public static class Unbound<S extends Structure> extends BCLStructure<S> {
private final Function<Structure.StructureSettings, S> structureBuilder; private final StructureBuilder<S> structureBuilder;
private final TerrainAdjustment terrainAdjustment; private final TerrainAdjustment terrainAdjustment;
private Bound<S> registered; private Bound<S> registered;
@ -36,7 +51,7 @@ public abstract class BCLStructure<S extends Structure> {
@NotNull StructurePlacement placement, @NotNull StructurePlacement placement,
@NotNull Codec<S> codec, @NotNull Codec<S> codec,
@NotNull TagKey<Biome> biomeTag, @NotNull TagKey<Biome> biomeTag,
@NotNull Function<Structure.StructureSettings, S> structureBuilder, @NotNull StructureBuilder<S> structureBuilder,
@NotNull TerrainAdjustment terrainAdjustment @NotNull TerrainAdjustment terrainAdjustment
) { ) {
super( super(
@ -58,12 +73,19 @@ public abstract class BCLStructure<S extends Structure> {
public Bound<S> register(BootstapContext<Structure> bootstrapContext) { public Bound<S> register(BootstapContext<Structure> bootstrapContext) {
if (registered != null) return registered; if (registered != null) return registered;
S baseStructure = structureBuilder.apply(structure( final Structure.StructureSettings settings = structure(
bootstrapContext, bootstrapContext,
this.biomeTag, this.biomeTag,
this.featureStep, this.featureStep,
terrainAdjustment terrainAdjustment
)); );
S baseStructure;
if (structureBuilder instanceof StructureBuilderWithContext<S> sctx) {
baseStructure = sctx.apply(settings, bootstrapContext);
} else {
baseStructure = structureBuilder.apply(settings);
}
Holder.Reference<Structure> structure = bootstrapContext.register(structureKey, baseStructure); Holder.Reference<Structure> structure = bootstrapContext.register(structureKey, baseStructure);
BCLStructureBuilder.UNBOUND_STRUCTURES.remove(this); BCLStructureBuilder.UNBOUND_STRUCTURES.remove(this);
registered = new Bound<>( registered = new Bound<>(

View file

@ -1,69 +1,45 @@
package org.betterx.bclib.api.v2.levelgen.structures; package org.betterx.bclib.api.v2.levelgen.structures;
import org.betterx.worlds.together.tag.v3.TagManager;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import net.minecraft.data.worldgen.BootstapContext; import net.minecraft.data.worldgen.BootstapContext;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureSet; import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.structure.TerrainAdjustment;
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType;
import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Function;
public class BCLStructureBuilder<S extends Structure> {
static final ConcurrentLinkedQueue<BCLStructure.Unbound<?>> UNBOUND_STRUCTURES = new ConcurrentLinkedQueue<>();
static final ConcurrentLinkedQueue<BCLStructure.Unbound<?>> UNBOUND_STRUCTURE_SETS = new ConcurrentLinkedQueue<>();
private final ResourceLocation structureID;
private final Function<Structure.StructureSettings, S> structureBuilder;
private GenerationStep.Decoration step;
public class BCLStructureBuilder<S extends Structure> extends BCLBaseStructureBuilder<S, BCLStructureBuilder<S>> {
private Codec<S> codec; private Codec<S> codec;
private StructurePlacement placement;
private TagKey<Biome> biomeTag;
private TerrainAdjustment terrainAdjustment;
private BCLStructureBuilder( private BCLStructureBuilder(
ResourceLocation structureID, ResourceLocation structureID,
Function<Structure.StructureSettings, S> structureBuilder BCLStructure.StructureBuilder<S> structureBuilder
) { ) {
this.structureID = structureID; super(structureID, structureBuilder);
this.structureBuilder = structureBuilder;
this.step = GenerationStep.Decoration.SURFACE_STRUCTURES; if (structureBuilder instanceof BCLStructure.StructureCodecProvider sctx) {
this.terrainAdjustment = TerrainAdjustment.NONE; this.codec = sctx.getCodec();
this.codec = null; } else {
this.placement = null; this.codec = Structure.simpleCodec((settings) -> structureBuilder.apply(settings));
this.biomeTag = null; }
}
public static BCLJigsawStructureBuilder jigsaw(
ResourceLocation structureID
) {
return new BCLJigsawStructureBuilder(structureID);
} }
public static <S extends Structure> BCLStructureBuilder<S> start( public static <S extends Structure> BCLStructureBuilder<S> start(
ResourceLocation structureID, ResourceLocation structureID,
Function<Structure.StructureSettings, S> structureBuilder BCLStructure.StructureBuilderWithContext<S> structureBuilder
) { ) {
return new BCLStructureBuilder<>(structureID, structureBuilder); return new BCLStructureBuilder<>(structureID, structureBuilder);
} }
public BCLStructureBuilder<S> adjustment(TerrainAdjustment value) { public static <S extends Structure> BCLStructureBuilder<S> start(
this.terrainAdjustment = value; ResourceLocation structureID,
return this; BCLStructure.StructureBuilder<S> structureBuilder
} ) {
return new BCLStructureBuilder<>(structureID, structureBuilder);
public BCLStructureBuilder<S> step(GenerationStep.Decoration value) {
this.step = value;
return this;
} }
public BCLStructureBuilder<S> codec(Codec<S> value) { public BCLStructureBuilder<S> codec(Codec<S> value) {
@ -71,59 +47,17 @@ public class BCLStructureBuilder<S extends Structure> {
return this; return this;
} }
public BCLStructureBuilder<S> placement(StructurePlacement value) { @Override
this.placement = value; protected Codec<S> getCodec() {
return this; return codec;
} }
public BCLStructureBuilder<S> randomPlacement(int spacing, int separation) {
this.placement = new RandomSpreadStructurePlacement(
spacing,
separation,
RandomSpreadType.LINEAR,
13323129 + spacing + separation + structureID.toString().hashCode() % 10000
);
return this;
}
public BCLStructureBuilder<S> biomeTag(String modID, String path) {
this.biomeTag = TagManager.BIOMES.makeStructureTag(modID, path);
return this;
}
public BCLStructureBuilder<S> biomeTag(TagKey<Biome> tag) {
this.biomeTag = tag;
return this;
}
public BCLStructure<S> build() {
if (placement == null) {
throw new IllegalStateException("Placement needs to be defined for " + this.structureID);
}
if (codec == null) codec(Structure.simpleCodec(structureBuilder));
if (biomeTag == null) biomeTag(structureID.getNamespace(), structureID.getPath());
var res = new BCLStructure.Unbound<>(
structureID,
step,
placement,
codec,
biomeTag,
structureBuilder,
terrainAdjustment
);
UNBOUND_STRUCTURES.add(res);
UNBOUND_STRUCTURE_SETS.add(res);
return res;
}
public static void registerUnbound(BootstapContext<Structure> context) { public static void registerUnbound(BootstapContext<Structure> context) {
UNBOUND_STRUCTURES.forEach(s -> s.register(context)); BCLBaseStructureBuilder.registerUnbound(context);
UNBOUND_STRUCTURES.clear();
} }
public static void registerUnboundSets(BootstapContext<StructureSet> context) { public static void registerUnboundSets(BootstapContext<StructureSet> context) {
UNBOUND_STRUCTURE_SETS.forEach(s -> s.registerSet(context)); BCLBaseStructureBuilder.registerUnboundSets(context);
UNBOUND_STRUCTURE_SETS.clear();
} }
} }

View file

@ -0,0 +1,25 @@
package org.betterx.bclib.api.v2.levelgen.structures;
import com.mojang.datafixers.util.Either;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
import java.util.function.Function;
public class StructurePools {
public static ResourceKey<StructureTemplatePool> createKey(ResourceLocation id) {
return ResourceKey.create(Registries.TEMPLATE_POOL, id);
}
public static Function<StructureTemplatePool.Projection, SinglePoolElement> single(
ResourceLocation id,
Holder<StructureProcessorList> holder
) {
return (projection) -> new SinglePoolElement(Either.left(id), holder, projection);
}
}

View file

@ -16,6 +16,7 @@ accessible class net/minecraft/server/dedicated/DedicatedServerProperties$Wo
accessible class net/minecraft/client/resources/model/AtlasSet$AtlasEntry accessible class net/minecraft/client/resources/model/AtlasSet$AtlasEntry
extendable class net/minecraft/world/level/block/state/properties/WoodType extendable class net/minecraft/world/level/block/state/properties/WoodType
#Methods #Methods
accessible method net/minecraft/world/level/storage/loot/LootPool <init> ([Lnet/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer;[Lnet/minecraft/world/level/storage/loot/predicates/LootItemCondition;[Lnet/minecraft/world/level/storage/loot/functions/LootItemFunction;Lnet/minecraft/world/level/storage/loot/providers/number/NumberProvider;Lnet/minecraft/world/level/storage/loot/providers/number/NumberProvider;)V accessible method net/minecraft/world/level/storage/loot/LootPool <init> ([Lnet/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer;[Lnet/minecraft/world/level/storage/loot/predicates/LootItemCondition;[Lnet/minecraft/world/level/storage/loot/functions/LootItemFunction;Lnet/minecraft/world/level/storage/loot/providers/number/NumberProvider;Lnet/minecraft/world/level/storage/loot/providers/number/NumberProvider;)V
accessible method net/minecraft/world/entity/ai/village/poi/PoiTypes register (Lnet/minecraft/core/Registry;Lnet/minecraft/resources/ResourceKey;Ljava/util/Set;II)Lnet/minecraft/world/entity/ai/village/poi/PoiType; accessible method net/minecraft/world/entity/ai/village/poi/PoiTypes register (Lnet/minecraft/core/Registry;Lnet/minecraft/resources/ResourceKey;Ljava/util/Set;II)Lnet/minecraft/world/entity/ai/village/poi/PoiType;
@ -32,6 +33,7 @@ accessible method net/minecraft/advancements/Advancement$Builder <init> (Z)V
accessible method net/minecraft/world/level/block/Blocks ocelotOrParrot (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/EntityType;)Ljava/lang/Boolean; accessible method net/minecraft/world/level/block/Blocks ocelotOrParrot (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/EntityType;)Ljava/lang/Boolean;
accessible method net/minecraft/world/level/block/Blocks never (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/EntityType;)Ljava/lang/Boolean; accessible method net/minecraft/world/level/block/Blocks never (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/EntityType;)Ljava/lang/Boolean;
accessible method net/minecraft/world/level/block/Blocks never (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z accessible method net/minecraft/world/level/block/Blocks never (Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z
accessible method net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement <init> (Lcom/mojang/datafixers/util/Either;Lnet/minecraft/core/Holder;Lnet/minecraft/world/level/levelgen/structure/pools/StructureTemplatePool$Projection;)V
#Fields #Fields
accessible field net/minecraft/world/entity/ai/village/poi/PoiTypes TYPE_BY_STATE Ljava/util/Map; accessible field net/minecraft/world/entity/ai/village/poi/PoiTypes TYPE_BY_STATE Ljava/util/Map;