diff --git a/src/main/java/org/betterx/bclib/BCLib.java b/src/main/java/org/betterx/bclib/BCLib.java index 4d5c71c1..66d4a803 100644 --- a/src/main/java/org/betterx/bclib/BCLib.java +++ b/src/main/java/org/betterx/bclib/BCLib.java @@ -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(); diff --git a/src/main/java/org/betterx/bclib/api/biomes/BiomeAPI.java b/src/main/java/org/betterx/bclib/api/biomes/BiomeAPI.java index 4938cc08..160e01fc 100644 --- a/src/main/java/org/betterx/bclib/api/biomes/BiomeAPI.java +++ b/src/main/java/org/betterx/bclib/api/biomes/BiomeAPI.java @@ -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, 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, 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 dimensionID, BiConsumer> modification) { List>> 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 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> modification) { List>> modifications = TAG_ADDERS.computeIfAbsent(dimensionID, - k -> Lists.newArrayList()); + k -> Lists.newArrayList()); modifications.add(modification); } @@ -718,9 +720,9 @@ public class BiomeAPI { } List>> modifications = MODIFICATIONS.get(level - .dimensionTypeRegistration() - .unwrapKey() - .orElseThrow()); + .dimensionTypeRegistration() + .unwrapKey() + .orElseThrow()); for (Holder 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) { -// BiomeGenerationSettings settings = biome.value().getGenerationSettings(); -// BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) settings; -// List> featureList = CollectionsUtil.getMutable(accessor.bclib_getFeatures()); -// final int size = featureList.size(); -// for (int i = 0; i < size; i++) { -// List> 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> featureList = CollectionsUtil.getMutable(accessor.bclib_getFeatures()); + final int size = featureList.size(); + for (int i = 0; i < size; i++) { + List> 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> features) { -// initFeatureOrder(); -// -// Set> 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> 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); + }); } diff --git a/src/main/java/org/betterx/bclib/util/BlocksHelper.java b/src/main/java/org/betterx/bclib/util/BlocksHelper.java index 396e1f21..a3b27542 100644 --- a/src/main/java/org/betterx/bclib/util/BlocksHelper.java +++ b/src/main/java/org/betterx/bclib/util/BlocksHelper.java @@ -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 findSurface(LevelAccessor level, - BlockPos startPos, - int minY, - Predicate surface) { + public static Optional findSurfaceBelow(LevelAccessor level, + BlockPos startPos, + int minY, + Predicate 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 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 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; + } } diff --git a/src/main/java/org/betterx/bclib/world/features/BCLFeature.java b/src/main/java/org/betterx/bclib/world/features/BCLFeature.java index 3f9679bd..ca4da6f7 100644 --- a/src/main/java/org/betterx/bclib/world/features/BCLFeature.java +++ b/src/main/java/org/betterx/bclib/world/features/BCLFeature.java @@ -55,18 +55,20 @@ public class BCLFeature { Holder> configuredFeature; if (!BuiltinRegistries.CONFIGURED_FEATURE.containsKey(id)) { configuredFeature = (Holder>) (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 > F register(String string, F feature) { + return Registry.register(Registry.FEATURE, string, feature); + } + /** * Get raw feature. * diff --git a/src/main/java/org/betterx/bclib/world/features/SurfaceFeature.java b/src/main/java/org/betterx/bclib/world/features/SurfaceFeature.java index 28ecc45c..bc6108a6 100644 --- a/src/main/java/org/betterx/bclib/world/features/SurfaceFeature.java +++ b/src/main/java/org/betterx/bclib/world/features/SurfaceFeature.java @@ -32,14 +32,14 @@ public abstract class SurfaceFeature extends Fea @Override public boolean place(FeaturePlaceContext ctx) { - Optional pos = BlocksHelper.findSurface(ctx.level(), + Optional 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; diff --git a/src/main/java/org/betterx/bclib/world/features/TemplateFeature.java b/src/main/java/org/betterx/bclib/world/features/TemplateFeature.java new file mode 100644 index 00000000..d721f423 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/features/TemplateFeature.java @@ -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 extends Feature { + public static final Feature INSTANCE = BCLFeature.register("template", + new TemplateFeature(TemplateFeatureConfig.CODEC)); + + public static 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 codec) { + super(codec); + } + + @Override + public boolean place(FeaturePlaceContext ctx) { + return ctx.config().structure.generate(ctx.level(), ctx.origin(), ctx.random()); + } +} diff --git a/src/main/java/org/betterx/bclib/world/features/TemplateFeatureConfig.java b/src/main/java/org/betterx/bclib/world/features/TemplateFeatureConfig.java new file mode 100644 index 00000000..18d2642e --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/features/TemplateFeatureConfig.java @@ -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 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); + } +} diff --git a/src/main/java/org/betterx/bclib/world/structures/BCLStructure.java b/src/main/java/org/betterx/bclib/world/structures/BCLStructure.java index 9f74e0df..c6cb99ff 100644 --- a/src/main/java/org/betterx/bclib/world/structures/BCLStructure.java +++ b/src/main/java/org/betterx/bclib/world/structures/BCLStructure.java @@ -59,7 +59,8 @@ public class BCLStructure { return structure(tagKey, Map.of(), decoration, terrainAdjustment); } - private static StructureType registerStructureType(ResourceLocation id, Codec codec) { + private static StructureType registerStructureType(ResourceLocation id, + Codec codec) { return Registry.register(Registry.STRUCTURE_TYPES, id, () -> codec); } @@ -93,9 +94,9 @@ public class BCLStructure { // 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 { QuartPos.fromBlock(blockPos.getY()), QuartPos.fromBlock(blockPos.getZ()), context.randomState().sampler() - ) - ); + ) + ); } public Holder getStructure() { diff --git a/src/main/java/org/betterx/bclib/world/structures/StructureNBT.java b/src/main/java/org/betterx/bclib/world/structures/StructureNBT.java new file mode 100644 index 00000000..fbb47d96 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/structures/StructureNBT.java @@ -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); + } +} diff --git a/src/main/java/org/betterx/bclib/world/structures/StructurePlacementType.java b/src/main/java/org/betterx/bclib/world/structures/StructurePlacementType.java new file mode 100644 index 00000000..adb48ad7 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/structures/StructurePlacementType.java @@ -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 CODEC = StringRepresentable.fromEnum(StructurePlacementType::values); + + public String getName() { + return this.getSerializedName(); + } + + @Override + public String getSerializedName() { + return this.name().toLowerCase(); + } +} diff --git a/src/main/java/org/betterx/bclib/world/structures/StructureWorldNBT.java b/src/main/java/org/betterx/bclib/world/structures/StructureWorldNBT.java new file mode 100644 index 00000000..dcd47285 --- /dev/null +++ b/src/main/java/org/betterx/bclib/world/structures/StructureWorldNBT.java @@ -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; + } +}