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();
DataExchangeAPI.registerDescriptors(List.of(
HelloClient.DESCRIPTOR,
HelloServer.DESCRIPTOR,
RequestFiles.DESCRIPTOR,
SendFiles.DESCRIPTOR,
Chunker.DESCRIPTOR
));
HelloClient.DESCRIPTOR,
HelloServer.DESCRIPTOR,
RequestFiles.DESCRIPTOR,
SendFiles.DESCRIPTOR,
Chunker.DESCRIPTOR
)
);
BCLibPatch.register();
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.WorldGenLevel;
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.Biomes;
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.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.mutable.MutableInt;
import org.betterx.bclib.BCLib;
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 END_MIDLANDS = registerSubBiome(THE_END,
getFromRegistry(Biomes.END_MIDLANDS).value(),
0.5F);
getFromRegistry(Biomes.END_MIDLANDS).value(),
0.5F);
public static final BCLBiome END_HIGHLANDS = registerSubBiome(THE_END,
getFromRegistry(Biomes.END_HIGHLANDS).value(),
0.5F);
getFromRegistry(Biomes.END_HIGHLANDS).value(),
0.5F);
public static final BCLBiome END_BARRENS = registerEndVoidBiome(getFromRegistry(new ResourceLocation("end_barrens")));
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) {
return registerSubBiome(parent,
subBiome,
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE));
subBiome,
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE));
}
public static BCLBiome registerSubBiome(BCLBiome parent, Biome subBiome, float genChance) {
return registerSubBiome(parent,
subBiome,
genChance,
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE));
subBiome,
genChance,
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE));
}
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) {
BCLBiome bclBiome = new BCLBiome(biome.value(),
VanillaBiomeSettings.createVanilla().setGenChance(genChance).build());
VanillaBiomeSettings.createVanilla().setGenChance(genChance).build());
registerBiome(bclBiome, BiomeType.OTHER_END_LAND);
return bclBiome;
@ -360,7 +362,7 @@ public class BiomeAPI {
*/
public static BCLBiome registerEndVoidBiome(Holder<Biome> biome, float genChance) {
BCLBiome bclBiome = new BCLBiome(biome.value(),
VanillaBiomeSettings.createVanilla().setGenChance(genChance).build());
VanillaBiomeSettings.createVanilla().setGenChance(genChance).build());
registerBiome(bclBiome, BiomeType.END_VOID);
return bclBiome;
@ -601,7 +603,7 @@ public class BiomeAPI {
public static void registerBiomeModification(ResourceKey<LevelStem> dimensionID,
BiConsumer<ResourceLocation, Holder<Biome>> modification) {
List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = MODIFICATIONS.computeIfAbsent(dimensionID,
k -> Lists.newArrayList());
k -> Lists.newArrayList());
modifications.add(modification);
}
@ -635,7 +637,7 @@ public class BiomeAPI {
/**
* For internal use only
*/
public static void _runTagAdders() {
public static void _runBiomeTagAdders() {
for (var mod : TAG_ADDERS.entrySet()) {
Stream<ResourceLocation> s = null;
if (mod.getKey() == Level.NETHER) s = BiomeType.BIOME_TYPE_MAP.entrySet()
@ -667,7 +669,7 @@ public class BiomeAPI {
public static void onFinishingBiomeTags(ResourceKey dimensionID,
BiConsumer<ResourceLocation, Holder<Biome>> modification) {
List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = TAG_ADDERS.computeIfAbsent(dimensionID,
k -> Lists.newArrayList());
k -> Lists.newArrayList());
modifications.add(modification);
}
@ -718,9 +720,9 @@ public class BiomeAPI {
}
List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = MODIFICATIONS.get(level
.dimensionTypeRegistration()
.unwrapKey()
.orElseThrow());
.dimensionTypeRegistration()
.unwrapKey()
.orElseThrow());
for (Holder<Biome> biomeHolder : biomes) {
if (biomeHolder.isBound()) {
applyModificationsAndUpdateFeatures(modifications, biomeHolder);
@ -773,16 +775,16 @@ public class BiomeAPI {
* @param biome The {@link Biome} to sort the features for
*/
public static void sortBiomeFeatures(Holder<Biome> biome) {
// BiomeGenerationSettings settings = biome.value().getGenerationSettings();
// BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) settings;
// List<HolderSet<PlacedFeature>> featureList = CollectionsUtil.getMutable(accessor.bclib_getFeatures());
// final int size = featureList.size();
// for (int i = 0; i < size; i++) {
// List<Holder<PlacedFeature>> features = getFeaturesListCopy(featureList, i);
// sortFeatures(features);
// featureList.set(i, HolderSet.direct(features));
// }
// accessor.bclib_setFeatures(featureList);
BiomeGenerationSettings settings = biome.value().getGenerationSettings();
BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) settings;
List<HolderSet<PlacedFeature>> featureList = CollectionsUtil.getMutable(accessor.bclib_getFeatures());
final int size = featureList.size();
for (int i = 0; i < size; i++) {
List<Holder<PlacedFeature>> features = getFeaturesListCopy(featureList, i);
sortFeatures(features);
featureList.set(i, HolderSet.direct(features));
}
accessor.bclib_setFeatures(featureList);
}
/**
@ -930,25 +932,25 @@ public class BiomeAPI {
}
private static void sortFeatures(List<Holder<PlacedFeature>> features) {
// initFeatureOrder();
//
// Set<Holder<PlacedFeature>> featuresWithoutDuplicates = Sets.newHashSet();
// features.forEach(holder -> featuresWithoutDuplicates.add(holder));
//
// if (featuresWithoutDuplicates.size() != features.size()) {
// features.clear();
// featuresWithoutDuplicates.forEach(feature -> features.add(feature));
// }
//
// features.forEach(feature -> {
// FEATURE_ORDER.computeIfAbsent(feature, f -> FEATURE_ORDER_ID.getAndIncrement());
// });
//
// features.sort((f1, f2) -> {
// int v1 = FEATURE_ORDER.getOrDefault(f1, 70000);
// int v2 = FEATURE_ORDER.getOrDefault(f2, 70000);
// return Integer.compare(v1, v2);
// });
initFeatureOrder();
Set<Holder<PlacedFeature>> featuresWithoutDuplicates = Sets.newHashSet();
features.forEach(holder -> featuresWithoutDuplicates.add(holder));
if (featuresWithoutDuplicates.size() != features.size()) {
features.clear();
featuresWithoutDuplicates.forEach(feature -> features.add(feature));
}
features.forEach(feature -> {
FEATURE_ORDER.computeIfAbsent(feature, f -> FEATURE_ORDER_ID.getAndIncrement());
});
features.sort((f1, f2) -> {
int v1 = FEATURE_ORDER.getOrDefault(f1, 70000);
int v2 = FEATURE_ORDER.getOrDefault(f2, 70000);
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.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.material.LavaFluid;
import com.google.common.collect.Maps;
@ -204,10 +205,10 @@ public class BlocksHelper {
}
}
public static Optional<BlockPos> findSurface(LevelAccessor level,
BlockPos startPos,
int minY,
Predicate<BlockState> surface) {
public static Optional<BlockPos> findSurfaceBelow(LevelAccessor level,
BlockPos startPos,
int minY,
Predicate<BlockState> surface) {
final MutableBlockPos POS = new MutableBlockPos(startPos.getX(), startPos.getY(), startPos.getZ());
for (int y = startPos.getY(); y >= minY; y--) {
POS.setY(y);
@ -215,4 +216,36 @@ public class BlocksHelper {
}
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;
if (!BuiltinRegistries.CONFIGURED_FEATURE.containsKey(id)) {
configuredFeature = (Holder<ConfiguredFeature<?, ?>>) (Object) FeatureUtils.register(id.toString(),
feature,
configuration);
feature,
configuration);
} else {
configuredFeature = BuiltinRegistries.CONFIGURED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.CONFIGURED_FEATURE.key(),
id)).orElseThrow();
configuredFeature = BuiltinRegistries.CONFIGURED_FEATURE
.getHolder(ResourceKey.create(BuiltinRegistries.CONFIGURED_FEATURE.key(),
id))
.orElseThrow();
}
if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) {
return PlacementUtils.register(id.toString(), configuredFeature, modifiers);
} else {
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();
}
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.
*

View file

@ -32,14 +32,14 @@ public abstract class SurfaceFeature<T extends FeatureConfiguration> extends Fea
@Override
public boolean place(FeaturePlaceContext<T> ctx) {
Optional<BlockPos> pos = BlocksHelper.findSurface(ctx.level(),
Optional<BlockPos> pos = BlocksHelper.findSurfaceBelow(ctx.level(),
ctx.origin(),
minHeight(ctx),
this::isValidSurface);
if (pos.isPresent()) {
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());
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);
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);
}
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);
}
@ -93,9 +94,9 @@ public class BCLStructure<S extends Structure> {
//
this.spreadConfig = new RandomSpreadStructurePlacement(spacing,
separation,
RandomSpreadType.LINEAR,
RANDOM.nextInt(8192));
separation,
RandomSpreadType.LINEAR,
RANDOM.nextInt(8192));
this.structureKey = ResourceKey.create(Registry.STRUCTURE_REGISTRY, id);
this.structureSetKey = ResourceKey.create(Registry.STRUCTURE_SET_REGISTRY, id);
this.structureType = registerStructureType(id, STRUCTURE_CODEC);
@ -136,8 +137,8 @@ public class BCLStructure<S extends Structure> {
QuartPos.fromBlock(blockPos.getY()),
QuartPos.fromBlock(blockPos.getZ()),
context.randomState().sampler()
)
);
)
);
}
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;
}
}