NBT Feature Handling

This commit is contained in:
Frank 2022-05-30 22:37:29 +02:00
parent 155d3663df
commit 95434107ec
11 changed files with 532 additions and 69 deletions

View file

@ -49,12 +49,13 @@ public class BCLib implements ModInitializer {
AnvilRecipe.register(); AnvilRecipe.register();
DataExchangeAPI.registerDescriptors(List.of( DataExchangeAPI.registerDescriptors(List.of(
HelloClient.DESCRIPTOR, HelloClient.DESCRIPTOR,
HelloServer.DESCRIPTOR, HelloServer.DESCRIPTOR,
RequestFiles.DESCRIPTOR, RequestFiles.DESCRIPTOR,
SendFiles.DESCRIPTOR, SendFiles.DESCRIPTOR,
Chunker.DESCRIPTOR Chunker.DESCRIPTOR
)); )
);
BCLibPatch.register(); BCLibPatch.register();
Configs.save(); Configs.save();

View file

@ -15,6 +15,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeGenerationSettings;
import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData;
@ -38,6 +39,7 @@ import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.lang3.mutable.MutableInt;
import org.betterx.bclib.BCLib; import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.tag.CommonBiomeTags; import org.betterx.bclib.api.tag.CommonBiomeTags;
@ -129,11 +131,11 @@ public class BiomeAPI {
public static final BCLBiome THE_END = registerEndLandBiome(getFromRegistry(Biomes.THE_END)); public static final BCLBiome THE_END = registerEndLandBiome(getFromRegistry(Biomes.THE_END));
public static final BCLBiome END_MIDLANDS = registerSubBiome(THE_END, public static final BCLBiome END_MIDLANDS = registerSubBiome(THE_END,
getFromRegistry(Biomes.END_MIDLANDS).value(), getFromRegistry(Biomes.END_MIDLANDS).value(),
0.5F); 0.5F);
public static final BCLBiome END_HIGHLANDS = registerSubBiome(THE_END, public static final BCLBiome END_HIGHLANDS = registerSubBiome(THE_END,
getFromRegistry(Biomes.END_HIGHLANDS).value(), getFromRegistry(Biomes.END_HIGHLANDS).value(),
0.5F); 0.5F);
public static final BCLBiome END_BARRENS = registerEndVoidBiome(getFromRegistry(new ResourceLocation("end_barrens"))); public static final BCLBiome END_BARRENS = registerEndVoidBiome(getFromRegistry(new ResourceLocation("end_barrens")));
public static final BCLBiome SMALL_END_ISLANDS = registerEndVoidBiome(getFromRegistry(new ResourceLocation( public static final BCLBiome SMALL_END_ISLANDS = registerEndVoidBiome(getFromRegistry(new ResourceLocation(
@ -216,15 +218,15 @@ public class BiomeAPI {
public static BCLBiome registerSubBiome(BCLBiome parent, BCLBiome subBiome) { public static BCLBiome registerSubBiome(BCLBiome parent, BCLBiome subBiome) {
return registerSubBiome(parent, return registerSubBiome(parent,
subBiome, subBiome,
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE)); BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE));
} }
public static BCLBiome registerSubBiome(BCLBiome parent, Biome subBiome, float genChance) { public static BCLBiome registerSubBiome(BCLBiome parent, Biome subBiome, float genChance) {
return registerSubBiome(parent, return registerSubBiome(parent,
subBiome, subBiome,
genChance, genChance,
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE)); BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE));
} }
public static BCLBiome registerSubBiome(BCLBiome parent, BCLBiome subBiome, BiomeType dim) { public static BCLBiome registerSubBiome(BCLBiome parent, BCLBiome subBiome, BiomeType dim) {
@ -312,7 +314,7 @@ public class BiomeAPI {
*/ */
public static BCLBiome registerEndLandBiome(Holder<Biome> biome, float genChance) { public static BCLBiome registerEndLandBiome(Holder<Biome> biome, float genChance) {
BCLBiome bclBiome = new BCLBiome(biome.value(), BCLBiome bclBiome = new BCLBiome(biome.value(),
VanillaBiomeSettings.createVanilla().setGenChance(genChance).build()); VanillaBiomeSettings.createVanilla().setGenChance(genChance).build());
registerBiome(bclBiome, BiomeType.OTHER_END_LAND); registerBiome(bclBiome, BiomeType.OTHER_END_LAND);
return bclBiome; return bclBiome;
@ -360,7 +362,7 @@ public class BiomeAPI {
*/ */
public static BCLBiome registerEndVoidBiome(Holder<Biome> biome, float genChance) { public static BCLBiome registerEndVoidBiome(Holder<Biome> biome, float genChance) {
BCLBiome bclBiome = new BCLBiome(biome.value(), BCLBiome bclBiome = new BCLBiome(biome.value(),
VanillaBiomeSettings.createVanilla().setGenChance(genChance).build()); VanillaBiomeSettings.createVanilla().setGenChance(genChance).build());
registerBiome(bclBiome, BiomeType.END_VOID); registerBiome(bclBiome, BiomeType.END_VOID);
return bclBiome; return bclBiome;
@ -601,7 +603,7 @@ public class BiomeAPI {
public static void registerBiomeModification(ResourceKey<LevelStem> dimensionID, public static void registerBiomeModification(ResourceKey<LevelStem> dimensionID,
BiConsumer<ResourceLocation, Holder<Biome>> modification) { BiConsumer<ResourceLocation, Holder<Biome>> modification) {
List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = MODIFICATIONS.computeIfAbsent(dimensionID, List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = MODIFICATIONS.computeIfAbsent(dimensionID,
k -> Lists.newArrayList()); k -> Lists.newArrayList());
modifications.add(modification); modifications.add(modification);
} }
@ -635,7 +637,7 @@ public class BiomeAPI {
/** /**
* For internal use only * For internal use only
*/ */
public static void _runTagAdders() { public static void _runBiomeTagAdders() {
for (var mod : TAG_ADDERS.entrySet()) { for (var mod : TAG_ADDERS.entrySet()) {
Stream<ResourceLocation> s = null; Stream<ResourceLocation> s = null;
if (mod.getKey() == Level.NETHER) s = BiomeType.BIOME_TYPE_MAP.entrySet() if (mod.getKey() == Level.NETHER) s = BiomeType.BIOME_TYPE_MAP.entrySet()
@ -667,7 +669,7 @@ public class BiomeAPI {
public static void onFinishingBiomeTags(ResourceKey dimensionID, public static void onFinishingBiomeTags(ResourceKey dimensionID,
BiConsumer<ResourceLocation, Holder<Biome>> modification) { BiConsumer<ResourceLocation, Holder<Biome>> modification) {
List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = TAG_ADDERS.computeIfAbsent(dimensionID, List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = TAG_ADDERS.computeIfAbsent(dimensionID,
k -> Lists.newArrayList()); k -> Lists.newArrayList());
modifications.add(modification); modifications.add(modification);
} }
@ -718,9 +720,9 @@ public class BiomeAPI {
} }
List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = MODIFICATIONS.get(level List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = MODIFICATIONS.get(level
.dimensionTypeRegistration() .dimensionTypeRegistration()
.unwrapKey() .unwrapKey()
.orElseThrow()); .orElseThrow());
for (Holder<Biome> biomeHolder : biomes) { for (Holder<Biome> biomeHolder : biomes) {
if (biomeHolder.isBound()) { if (biomeHolder.isBound()) {
applyModificationsAndUpdateFeatures(modifications, biomeHolder); applyModificationsAndUpdateFeatures(modifications, biomeHolder);
@ -773,16 +775,16 @@ public class BiomeAPI {
* @param biome The {@link Biome} to sort the features for * @param biome The {@link Biome} to sort the features for
*/ */
public static void sortBiomeFeatures(Holder<Biome> biome) { public static void sortBiomeFeatures(Holder<Biome> biome) {
// BiomeGenerationSettings settings = biome.value().getGenerationSettings(); BiomeGenerationSettings settings = biome.value().getGenerationSettings();
// BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) settings; BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) settings;
// List<HolderSet<PlacedFeature>> featureList = CollectionsUtil.getMutable(accessor.bclib_getFeatures()); List<HolderSet<PlacedFeature>> featureList = CollectionsUtil.getMutable(accessor.bclib_getFeatures());
// final int size = featureList.size(); final int size = featureList.size();
// for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
// List<Holder<PlacedFeature>> features = getFeaturesListCopy(featureList, i); List<Holder<PlacedFeature>> features = getFeaturesListCopy(featureList, i);
// sortFeatures(features); sortFeatures(features);
// featureList.set(i, HolderSet.direct(features)); featureList.set(i, HolderSet.direct(features));
// } }
// accessor.bclib_setFeatures(featureList); accessor.bclib_setFeatures(featureList);
} }
/** /**
@ -930,25 +932,25 @@ public class BiomeAPI {
} }
private static void sortFeatures(List<Holder<PlacedFeature>> features) { private static void sortFeatures(List<Holder<PlacedFeature>> features) {
// initFeatureOrder(); initFeatureOrder();
//
// Set<Holder<PlacedFeature>> featuresWithoutDuplicates = Sets.newHashSet(); Set<Holder<PlacedFeature>> featuresWithoutDuplicates = Sets.newHashSet();
// features.forEach(holder -> featuresWithoutDuplicates.add(holder)); features.forEach(holder -> featuresWithoutDuplicates.add(holder));
//
// if (featuresWithoutDuplicates.size() != features.size()) { if (featuresWithoutDuplicates.size() != features.size()) {
// features.clear(); features.clear();
// featuresWithoutDuplicates.forEach(feature -> features.add(feature)); featuresWithoutDuplicates.forEach(feature -> features.add(feature));
// } }
//
// features.forEach(feature -> { features.forEach(feature -> {
// FEATURE_ORDER.computeIfAbsent(feature, f -> FEATURE_ORDER_ID.getAndIncrement()); FEATURE_ORDER.computeIfAbsent(feature, f -> FEATURE_ORDER_ID.getAndIncrement());
// }); });
//
// features.sort((f1, f2) -> { features.sort((f1, f2) -> {
// int v1 = FEATURE_ORDER.getOrDefault(f1, 70000); int v1 = FEATURE_ORDER.getOrDefault(f1, 70000);
// int v2 = FEATURE_ORDER.getOrDefault(f2, 70000); int v2 = FEATURE_ORDER.getOrDefault(f2, 70000);
// return Integer.compare(v1, v2); return Integer.compare(v1, v2);
// }); });
} }

View file

@ -13,6 +13,7 @@ import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.material.LavaFluid;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -204,10 +205,10 @@ public class BlocksHelper {
} }
} }
public static Optional<BlockPos> findSurface(LevelAccessor level, public static Optional<BlockPos> findSurfaceBelow(LevelAccessor level,
BlockPos startPos, BlockPos startPos,
int minY, int minY,
Predicate<BlockState> surface) { Predicate<BlockState> surface) {
final MutableBlockPos POS = new MutableBlockPos(startPos.getX(), startPos.getY(), startPos.getZ()); final MutableBlockPos POS = new MutableBlockPos(startPos.getX(), startPos.getY(), startPos.getZ());
for (int y = startPos.getY(); y >= minY; y--) { for (int y = startPos.getY(); y >= minY; y--) {
POS.setY(y); POS.setY(y);
@ -215,4 +216,36 @@ public class BlocksHelper {
} }
return Optional.empty(); return Optional.empty();
} }
public static boolean findSurface(LevelAccessor level,
MutableBlockPos startPos,
Direction dir,
int length,
Predicate<BlockState> surface) {
for (int len = 0; len < length; len++) {
if (surface.test(level.getBlockState(startPos))) return true;
startPos.move(dir, 1);
}
return false;
}
public static boolean isFreeSpace(LevelAccessor level,
BlockPos startPos,
Direction dir,
int length,
Predicate<BlockState> freeSurface) {
MutableBlockPos POS = startPos.mutable();
for (int len = 1; len < length; len++) {
POS.move(dir, 1);
if (!freeSurface.test(level.getBlockState(POS))) {
return false;
}
}
return true;
}
public static boolean isLava(BlockState state) {
return state.getFluidState().getType() instanceof LavaFluid;
}
} }

View file

@ -55,18 +55,20 @@ public class BCLFeature {
Holder<ConfiguredFeature<?, ?>> configuredFeature; Holder<ConfiguredFeature<?, ?>> configuredFeature;
if (!BuiltinRegistries.CONFIGURED_FEATURE.containsKey(id)) { if (!BuiltinRegistries.CONFIGURED_FEATURE.containsKey(id)) {
configuredFeature = (Holder<ConfiguredFeature<?, ?>>) (Object) FeatureUtils.register(id.toString(), configuredFeature = (Holder<ConfiguredFeature<?, ?>>) (Object) FeatureUtils.register(id.toString(),
feature, feature,
configuration); configuration);
} else { } else {
configuredFeature = BuiltinRegistries.CONFIGURED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.CONFIGURED_FEATURE.key(), configuredFeature = BuiltinRegistries.CONFIGURED_FEATURE
id)).orElseThrow(); .getHolder(ResourceKey.create(BuiltinRegistries.CONFIGURED_FEATURE.key(),
id))
.orElseThrow();
} }
if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) { if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) {
return PlacementUtils.register(id.toString(), configuredFeature, modifiers); return PlacementUtils.register(id.toString(), configuredFeature, modifiers);
} else { } else {
return BuiltinRegistries.PLACED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.PLACED_FEATURE.key(), return BuiltinRegistries.PLACED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.PLACED_FEATURE.key(),
id)).orElseThrow(); id)).orElseThrow();
} }
} }
@ -79,6 +81,10 @@ public class BCLFeature {
return optional.isPresent(); return optional.isPresent();
} }
public static <C extends FeatureConfiguration, F extends Feature<C>> F register(String string, F feature) {
return Registry.register(Registry.FEATURE, string, feature);
}
/** /**
* Get raw feature. * Get raw feature.
* *

View file

@ -32,14 +32,14 @@ public abstract class SurfaceFeature<T extends FeatureConfiguration> extends Fea
@Override @Override
public boolean place(FeaturePlaceContext<T> ctx) { public boolean place(FeaturePlaceContext<T> ctx) {
Optional<BlockPos> pos = BlocksHelper.findSurface(ctx.level(), Optional<BlockPos> pos = BlocksHelper.findSurfaceBelow(ctx.level(),
ctx.origin(), ctx.origin(),
minHeight(ctx), minHeight(ctx),
this::isValidSurface); this::isValidSurface);
if (pos.isPresent()) { if (pos.isPresent()) {
int y2 = ctx.level().getHeight(Heightmap.Types.WORLD_SURFACE_WG, ctx.origin().getX(), ctx.origin().getZ()); int y2 = ctx.level().getHeight(Heightmap.Types.WORLD_SURFACE_WG, ctx.origin().getX(), ctx.origin().getZ());
int y3 = ctx.level().getHeight(Heightmap.Types.WORLD_SURFACE, ctx.origin().getX(), ctx.origin().getZ()); int y3 = ctx.level().getHeight(Heightmap.Types.WORLD_SURFACE, ctx.origin().getX(), ctx.origin().getZ());
System.out.println("Surfaces:" + pos.get().getY() + ", " + y2 + ", " + y3); System.out.println("Surfaces:" + pos.get().getY() + ", " + y2 + ", " + y3 + ", " + ctx.origin().getY());
generate(pos.get(), ctx); generate(pos.get(), ctx);
return true; return true;

View file

@ -0,0 +1,45 @@
package org.betterx.bclib.world.features;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.placement.BiomeFilter;
import net.minecraft.world.level.levelgen.placement.EnvironmentScanPlacement;
import com.mojang.serialization.Codec;
import org.betterx.bclib.api.features.BCLFeatureBuilder;
public class TemplateFeature<FC extends TemplateFeatureConfig> extends Feature<FC> {
public static final Feature<TemplateFeatureConfig> INSTANCE = BCLFeature.register("template",
new TemplateFeature(TemplateFeatureConfig.CODEC));
public static <T extends TemplateFeatureConfig> BCLFeature createAndRegister(TemplateFeatureConfig configuration,
int onveEveryChunk) {
return BCLFeatureBuilder
.start(new ResourceLocation(configuration.structure.location.getNamespace(),
"feature_" + configuration.structure.location.getPath()), INSTANCE)
.decoration(GenerationStep.Decoration.SURFACE_STRUCTURES)
.oncePerChunks(onveEveryChunk)
.squarePlacement()
.distanceToTopAndBottom10()
.modifier(EnvironmentScanPlacement.scanningFor(Direction.DOWN,
BlockPredicate.solid(),
BlockPredicate.matchesBlocks(Blocks.AIR, Blocks.WATER, Blocks.LAVA),
12))
.modifier(BiomeFilter.biome())
.buildAndRegister(configuration);
}
public TemplateFeature(Codec<FC> codec) {
super(codec);
}
@Override
public boolean place(FeaturePlaceContext<FC> ctx) {
return ctx.config().structure.generate(ctx.level(), ctx.origin(), ctx.random());
}
}

View file

@ -0,0 +1,36 @@
package org.betterx.bclib.world.features;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import org.betterx.bclib.world.structures.StructurePlacementType;
import org.betterx.bclib.world.structures.StructureWorldNBT;
public class TemplateFeatureConfig implements FeatureConfiguration {
public static final Codec<TemplateFeatureConfig> CODEC = RecordCodecBuilder.create((instance) -> instance
.group(ResourceLocation.CODEC
.fieldOf("location")
.forGetter((TemplateFeatureConfig cfg) -> cfg.structure.location),
Codec
.INT
.fieldOf("offset_y")
.orElse(0)
.forGetter((TemplateFeatureConfig cfg) -> cfg.structure.offsetY),
StructurePlacementType.CODEC
.fieldOf("placement")
.orElse(StructurePlacementType.FLOOR)
.forGetter((TemplateFeatureConfig cfg) -> cfg.structure.type)
)
.apply(instance, TemplateFeatureConfig::new)
);
public final StructureWorldNBT structure;
public TemplateFeatureConfig(ResourceLocation location, int offsetY, StructurePlacementType type) {
structure = new StructureWorldNBT(location, offsetY, type);
}
}

View file

@ -59,7 +59,8 @@ public class BCLStructure<S extends Structure> {
return structure(tagKey, Map.of(), decoration, terrainAdjustment); return structure(tagKey, Map.of(), decoration, terrainAdjustment);
} }
private static <S extends Structure> StructureType<S> registerStructureType(ResourceLocation id, Codec<S> codec) { private static <S extends Structure> StructureType<S> registerStructureType(ResourceLocation id,
Codec<S> codec) {
return Registry.register(Registry.STRUCTURE_TYPES, id, () -> codec); return Registry.register(Registry.STRUCTURE_TYPES, id, () -> codec);
} }
@ -93,9 +94,9 @@ public class BCLStructure<S extends Structure> {
// //
this.spreadConfig = new RandomSpreadStructurePlacement(spacing, this.spreadConfig = new RandomSpreadStructurePlacement(spacing,
separation, separation,
RandomSpreadType.LINEAR, RandomSpreadType.LINEAR,
RANDOM.nextInt(8192)); RANDOM.nextInt(8192));
this.structureKey = ResourceKey.create(Registry.STRUCTURE_REGISTRY, id); this.structureKey = ResourceKey.create(Registry.STRUCTURE_REGISTRY, id);
this.structureSetKey = ResourceKey.create(Registry.STRUCTURE_SET_REGISTRY, id); this.structureSetKey = ResourceKey.create(Registry.STRUCTURE_SET_REGISTRY, id);
this.structureType = registerStructureType(id, STRUCTURE_CODEC); this.structureType = registerStructureType(id, STRUCTURE_CODEC);
@ -136,8 +137,8 @@ public class BCLStructure<S extends Structure> {
QuartPos.fromBlock(blockPos.getY()), QuartPos.fromBlock(blockPos.getY()),
QuartPos.fromBlock(blockPos.getZ()), QuartPos.fromBlock(blockPos.getZ()),
context.randomState().sampler() context.randomState().sampler()
) )
); );
} }
public Holder<Structure> getStructure() { public Holder<Structure> getStructure() {

View file

@ -0,0 +1,125 @@
package org.betterx.bclib.world.structures;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import org.betterx.bclib.BCLib;
import java.io.IOException;
import java.io.InputStream;
public class StructureNBT {
public final ResourceLocation location;
protected StructureTemplate structure;
protected Mirror mirror = Mirror.NONE;
protected Rotation rotation = Rotation.NONE;
public StructureNBT(ResourceLocation location) {
this.location = location;
this.structure = readStructureFromJar(location);
}
protected StructureNBT(ResourceLocation location, StructureTemplate structure) {
this.location = location;
this.structure = structure;
}
public StructureNBT setRotation(Rotation rotation) {
this.rotation = rotation;
return this;
}
public Mirror getMirror() {
return mirror;
}
public StructureNBT setMirror(Mirror mirror) {
this.mirror = mirror;
return this;
}
public void randomRM(RandomSource random) {
rotation = Rotation.values()[random.nextInt(4)];
mirror = Mirror.values()[random.nextInt(3)];
}
public boolean generateCentered(ServerLevelAccessor world, BlockPos pos) {
if (structure == null) {
BCLib.LOGGER.error("No structure: " + location.toString());
return false;
}
MutableBlockPos blockpos2 = new MutableBlockPos().set(structure.getSize());
if (this.mirror == Mirror.FRONT_BACK)
blockpos2.setX(-blockpos2.getX());
if (this.mirror == Mirror.LEFT_RIGHT)
blockpos2.setZ(-blockpos2.getZ());
blockpos2.set(blockpos2.rotate(rotation));
StructurePlaceSettings data = new StructurePlaceSettings().setRotation(this.rotation).setMirror(this.mirror);
BlockPos newPos = pos.offset(-blockpos2.getX() >> 1, 0, -blockpos2.getZ() >> 1);
structure.placeInWorld(
world,
newPos,
newPos,
data,
world.getRandom(),
Block.UPDATE_CLIENTS
);
return true;
}
private static StructureTemplate readStructureFromJar(ResourceLocation resource) {
String ns = resource.getNamespace();
String nm = resource.getPath();
try {
InputStream inputstream = MinecraftServer.class.getResourceAsStream("/data/" + ns + "/structures/" + nm + ".nbt");
return readStructureFromStream(inputstream);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static StructureTemplate readStructureFromStream(InputStream stream) throws IOException {
CompoundTag nbttagcompound = NbtIo.readCompressed(stream);
StructureTemplate template = new StructureTemplate();
template.load(nbttagcompound);
return template;
}
public BlockPos getSize() {
if (rotation == Rotation.NONE || rotation == Rotation.CLOCKWISE_180)
return new BlockPos(structure.getSize());
else {
Vec3i size = structure.getSize();
int x = size.getX();
int z = size.getZ();
return new BlockPos(z, size.getY(), x);
}
}
public String getName() {
return location.getPath();
}
public BoundingBox getBoundingBox(BlockPos pos) {
return structure.getBoundingBox(new StructurePlaceSettings().setRotation(this.rotation).setMirror(mirror), pos);
}
}

View file

@ -0,0 +1,20 @@
package org.betterx.bclib.world.structures;
import net.minecraft.util.StringRepresentable;
import com.mojang.serialization.Codec;
public enum StructurePlacementType implements StringRepresentable {
FLOOR, WALL, CEIL, LAVA, UNDER;
public static final Codec<StructurePlacementType> CODEC = StringRepresentable.fromEnum(StructurePlacementType::values);
public String getName() {
return this.getSerializedName();
}
@Override
public String getSerializedName() {
return this.name().toLowerCase();
}
}

View file

@ -0,0 +1,194 @@
package org.betterx.bclib.world.structures;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Blocks;
import org.betterx.bclib.util.BlocksHelper;
public class StructureWorldNBT extends StructureNBT {
public final StructurePlacementType type;
public final int offsetY;
public StructureWorldNBT(ResourceLocation location, int offsetY, StructurePlacementType type) {
super(location);
this.offsetY = offsetY;
this.type = type;
}
public boolean generate(ServerLevelAccessor level, BlockPos pos, RandomSource random) {
randomRM(random);
if (canGenerate(level, pos)) {
return generateCentered(level, pos.above(offsetY));
}
return false;
}
protected boolean canGenerate(LevelAccessor world, BlockPos pos) {
if (type == StructurePlacementType.FLOOR)
return canGenerateFloor(world, pos);
else if (type == StructurePlacementType.LAVA)
return canGenerateLava(world, pos);
else if (type == StructurePlacementType.UNDER)
return canGenerateUnder(world, pos);
else if (type == StructurePlacementType.CEIL)
return canGenerateCeil(world, pos);
else
return false;
}
private boolean containsBedrock(LevelAccessor level, BlockPos startPos) {
for (int i = 0; i < this.structure.getSize().getY(); i += 2) {
if (level.getBlockState(startPos.above(i)).is(Blocks.BEDROCK)) {
return true;
}
}
return false;
}
protected boolean canGenerateFloor(LevelAccessor world, BlockPos pos) {
if (containsBedrock(world, pos)) return false;
return getAirFraction(world, pos) > 0.6 && getAirFractionFoundation(world, pos) < 0.5;
}
protected boolean canGenerateLava(LevelAccessor world, BlockPos pos) {
if (containsBedrock(world, pos)) return false;
return getLavaFractionFoundation(world, pos) > 0.9 && getAirFraction(world, pos) > 0.9;
}
protected boolean canGenerateUnder(LevelAccessor world, BlockPos pos) {
if (containsBedrock(world, pos)) return false;
return getAirFraction(world, pos) < 0.2;
}
protected boolean canGenerateCeil(LevelAccessor world, BlockPos pos) {
if (containsBedrock(world, pos)) return false;
return getAirFractionBottom(world, pos) > 0.8 && getAirFraction(world, pos) < 0.6;
}
protected float getAirFraction(LevelAccessor world, BlockPos pos) {
final MutableBlockPos POS = new MutableBlockPos();
int airCount = 0;
MutableBlockPos size = new MutableBlockPos().set(new BlockPos(structure.getSize()).rotate(rotation));
size.setX(Math.abs(size.getX()));
size.setZ(Math.abs(size.getZ()));
BlockPos start = pos.offset(-(size.getX() >> 1), 0, -(size.getZ() >> 1));
BlockPos end = pos.offset(size.getX() >> 1, size.getY() + offsetY, size.getZ() >> 1);
int count = 0;
for (int x = start.getX(); x <= end.getX(); x++) {
POS.setX(x);
for (int y = start.getY(); y <= end.getY(); y++) {
POS.setY(y);
for (int z = start.getZ(); z <= end.getZ(); z++) {
POS.setZ(z);
if (world.isEmptyBlock(POS))
airCount++;
count++;
}
}
}
return (float) airCount / count;
}
private float getLavaFractionFoundation(LevelAccessor world, BlockPos pos) {
final MutableBlockPos POS = new MutableBlockPos();
int lavaCount = 0;
MutableBlockPos size = new MutableBlockPos().set(new BlockPos(structure.getSize()).rotate(rotation));
size.setX(Math.abs(size.getX()));
size.setZ(Math.abs(size.getZ()));
BlockPos start = pos.offset(-(size.getX() >> 1), 0, -(size.getZ() >> 1));
BlockPos end = pos.offset(size.getX() >> 1, 0, size.getZ() >> 1);
int count = 0;
POS.setY(pos.getY() - 1);
for (int x = start.getX(); x <= end.getX(); x++) {
POS.setX(x);
for (int z = start.getZ(); z <= end.getZ(); z++) {
POS.setZ(z);
if (BlocksHelper.isLava(world.getBlockState(POS)))
lavaCount++;
count++;
}
}
return (float) lavaCount / count;
}
private float getAirFractionFoundation(LevelAccessor world, BlockPos pos) {
final MutableBlockPos POS = new MutableBlockPos();
int airCount = 0;
MutableBlockPos size = new MutableBlockPos().set(new BlockPos(structure.getSize()).rotate(rotation));
size.setX(Math.abs(size.getX()));
size.setZ(Math.abs(size.getZ()));
BlockPos start = pos.offset(-(size.getX() >> 1), -1, -(size.getZ() >> 1));
BlockPos end = pos.offset(size.getX() >> 1, 0, size.getZ() >> 1);
int count = 0;
for (int x = start.getX(); x <= end.getX(); x++) {
POS.setX(x);
for (int y = start.getY(); y <= end.getY(); y++) {
POS.setY(y);
for (int z = start.getZ(); z <= end.getZ(); z++) {
POS.setZ(z);
if (world.getBlockState(POS).getMaterial().isReplaceable())
airCount++;
count++;
}
}
}
return (float) airCount / count;
}
private float getAirFractionBottom(LevelAccessor world, BlockPos pos) {
final MutableBlockPos POS = new MutableBlockPos();
int airCount = 0;
MutableBlockPos size = new MutableBlockPos().set(new BlockPos(structure.getSize()).rotate(rotation));
size.setX(Math.abs(size.getX()));
size.setZ(Math.abs(size.getZ()));
float y1 = Math.min(offsetY, 0);
float y2 = Math.max(offsetY, 0);
BlockPos start = pos.offset(-(size.getX() >> 1), y1, -(size.getZ() >> 1));
BlockPos end = pos.offset(size.getX() >> 1, y2, size.getZ() >> 1);
int count = 0;
for (int x = start.getX(); x <= end.getX(); x++) {
POS.setX(x);
for (int y = start.getY(); y <= end.getY(); y++) {
POS.setY(y);
for (int z = start.getZ(); z <= end.getZ(); z++) {
POS.setZ(z);
if (world.getBlockState(POS).getMaterial().isReplaceable())
airCount++;
count++;
}
}
}
return (float) airCount / count;
}
public boolean loaded() {
return structure != null;
}
}