diff --git a/src/main/java/ru/bclib/BCLib.java b/src/main/java/ru/bclib/BCLib.java index 1189f4e9..52a92253 100644 --- a/src/main/java/ru/bclib/BCLib.java +++ b/src/main/java/ru/bclib/BCLib.java @@ -4,13 +4,18 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; import ru.bclib.util.Logger; +import ru.bclib.api.BCLibTags; +import ru.bclib.api.SurfaceBuilders; public class BCLib implements ModInitializer { public static final String MOD_ID = "bclib"; public static final Logger LOGGER = new Logger(MOD_ID); @Override - public void onInitialize() {} + public void onInitialize() { + SurfaceBuilders.register(); + BCLibTags.init(); + } public static boolean isDevEnvironment() { return FabricLoader.getInstance().isDevelopmentEnvironment(); diff --git a/src/main/java/ru/bclib/api/BCLibTags.java b/src/main/java/ru/bclib/api/BCLibTags.java new file mode 100644 index 00000000..d27e5676 --- /dev/null +++ b/src/main/java/ru/bclib/api/BCLibTags.java @@ -0,0 +1,131 @@ +package ru.bclib.api; + +import java.util.function.Supplier; + +import net.fabricmc.fabric.api.tag.TagRegistry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.ItemTags; +import net.minecraft.tags.Tag; +import net.minecraft.tags.Tag.Named; +import net.minecraft.tags.TagCollection; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import ru.bclib.BCLib; +import ru.bclib.util.TagHelper; + +public class BCLibTags { + // Block Tags + public static final Tag.Named BOOKSHELVES = makeCommonBlockTag("bookshelves"); + public static final Tag.Named GEN_TERRAIN = makeBlockTag(BCLib.MOD_ID, "gen_terrain"); + public static final Tag.Named NETHER_GROUND = makeBlockTag(BCLib.MOD_ID, "nether_ground"); + public static final Tag.Named END_GROUND = makeBlockTag(BCLib.MOD_ID, "end_ground"); + + public static final Tag.Named BLOCK_CHEST = makeCommonBlockTag("chest"); + public static final Tag.Named END_STONES = makeCommonBlockTag("end_stones"); + public static final Tag.Named NETHER_STONES = makeCommonBlockTag("nether_stones"); + + public static final Tag.Named DRAGON_IMMUNE = getMCBlockTag("dragon_immune"); + + // Item Tags + public static final Tag.Named ITEM_CHEST = makeCommonItemTag("chest"); + public static final Tag.Named IRON_INGOTS = makeCommonItemTag("iron_ingots"); + public static final Tag.Named FURNACES = makeCommonItemTag("furnaces"); + public final static Tag.Named HAMMERS = makeItemTag("fabric", "hammers"); + + /** + * Get or create {@link Tag.Named}. + * @param containerSupplier - {@link TagCollection} {@link Supplier} tag collection; + * @param id - {@link ResourceLocation} tag id. + * @return {@link Tag.Named}. + */ + public static Tag.Named makeTag(Supplier> containerSupplier, ResourceLocation id) { + Tag tag = containerSupplier.get().getTag(id); + return tag == null ? TagRegistry.create(id, containerSupplier) : (Named) tag; + } + + /** + * Get or create {@link Block} {@link Tag.Named} with mod namespace. + * @param modID - {@link String} mod namespace (mod id); + * @param name - {@link String} tag name. + * @return {@link Block} {@link Tag.Named}. + */ + public static Tag.Named makeBlockTag(String modID, String name) { + return makeTag(BlockTags::getAllTags, new ResourceLocation(modID, name)); + } + + /** + * Get or create {@link Item} {@link Tag.Named} with mod namespace. + * @param modID - {@link String} mod namespace (mod id); + * @param name - {@link String} tag name. + * @return {@link Item} {@link Tag.Named}. + */ + public static Tag.Named makeItemTag(String modID, String name) { + return makeTag(ItemTags::getAllTags, new ResourceLocation(modID, name)); + } + + /** + * Get or create {@link Block} {@link Tag.Named}. + * @see Fabric Wiki (Tags) + * @param name - {@link String} tag name. + * @return {@link Block} {@link Tag.Named}. + */ + public static Tag.Named makeCommonBlockTag(String name) { + return makeTag(BlockTags::getAllTags, new ResourceLocation("c", name)); + } + + /** + * Get or create {@link Item} {@link Tag.Named}. + * @see Fabric Wiki (Tags) + * @param name - {@link String} tag name. + * @return {@link Item} {@link Tag.Named}. + */ + public static Tag.Named makeCommonItemTag(String name) { + return makeTag(ItemTags::getAllTags, new ResourceLocation("c", name)); + } + + /** + * Get or create Minecraft {@link Block} {@link Tag.Named}. + * @param name - {@link String} tag name. + * @return {@link Block} {@link Tag.Named}. + */ + public static Tag.Named getMCBlockTag(String name) { + ResourceLocation id = new ResourceLocation(name); + Tag tag = BlockTags.getAllTags().getTag(id); + return tag == null ? (Named) TagRegistry.block(id) : (Named) tag; + } + + /** + * Adds {@link Block} to NETHER_GROUND and GEN_TERRAIN tags to process it properly in terrain generators and block logic. + * @param block - {@link Block}. + */ + public static void addNetherGround(Block block) { + TagHelper.addTag(NETHER_GROUND, block); + TagHelper.addTag(GEN_TERRAIN, block); + } + + /** + * Adds {@link Block} to END_GROUND and GEN_TERRAIN tags to process it properly in terrain generators and block logic. + * @param block - {@link Block}. + */ + public static void addEndGround(Block block) { + TagHelper.addTag(GEN_TERRAIN, block); + TagHelper.addTag(END_GROUND, block); + } + + /** + * Initializes basic tags. Should be called only in BCLib main class. + */ + public static void init() { + TagHelper.addTag(BOOKSHELVES, Blocks.BOOKSHELF); + TagHelper.addTag(GEN_TERRAIN, Blocks.END_STONE, Blocks.NETHERRACK, Blocks.SOUL_SAND, Blocks.SOUL_SOIL); + TagHelper.addTag(NETHER_GROUND, Blocks.NETHERRACK, Blocks.SOUL_SAND, Blocks.SOUL_SOIL); + TagHelper.addTag(END_GROUND, Blocks.END_STONE); + TagHelper.addTag(BLOCK_CHEST, Blocks.CHEST); + TagHelper.addTag(ITEM_CHEST, Items.CHEST); + TagHelper.addTag(IRON_INGOTS, Items.IRON_INGOT); + TagHelper.addTag(FURNACES, Blocks.FURNACE); + } +} diff --git a/src/main/java/ru/bclib/api/SurfaceBuilders.java b/src/main/java/ru/bclib/api/SurfaceBuilders.java new file mode 100644 index 00000000..cdbf6e36 --- /dev/null +++ b/src/main/java/ru/bclib/api/SurfaceBuilders.java @@ -0,0 +1,20 @@ +package ru.bclib.api; + +import net.minecraft.core.Registry; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilder; +import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilderBaseConfiguration; + +public class SurfaceBuilders { + public static SurfaceBuilder register(String name, SurfaceBuilder builder) { + return Registry.register(Registry.SURFACE_BUILDER, name, builder); + } + + public static SurfaceBuilderBaseConfiguration makeSimpleConfig(Block block) { + BlockState state = block.defaultBlockState(); + return new SurfaceBuilderBaseConfiguration(state, state, state); + } + + public static void register() {} +} diff --git a/src/main/java/ru/bclib/mixin/common/TagLoaderMixin.java b/src/main/java/ru/bclib/mixin/common/TagLoaderMixin.java new file mode 100644 index 00000000..743a59f5 --- /dev/null +++ b/src/main/java/ru/bclib/mixin/common/TagLoaderMixin.java @@ -0,0 +1,26 @@ +package ru.bclib.mixin.common; + +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.Tag; +import net.minecraft.tags.TagLoader; +import ru.bclib.util.TagHelper; + +@Mixin(TagLoader.class) +public class TagLoaderMixin { + @Shadow + private String name; + + @ModifyArg(method = "prepare", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;supplyAsync(Ljava/util/function/Supplier;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;")) + public Supplier> be_modifyTags(Supplier> supplier, Executor executor) { + return () -> TagHelper.apply(name, supplier.get()); + } +} diff --git a/src/main/java/ru/bclib/util/BlocksHelper.java b/src/main/java/ru/bclib/util/BlocksHelper.java index b4940728..2282344d 100644 --- a/src/main/java/ru/bclib/util/BlocksHelper.java +++ b/src/main/java/ru/bclib/util/BlocksHelper.java @@ -1,14 +1,15 @@ package ru.bclib.util; -import java.util.HashSet; -import java.util.Iterator; import java.util.Map; import java.util.Random; + import com.google.common.collect.Maps; + import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos.MutableBlockPos; import net.minecraft.core.Direction; -import net.minecraft.core.Vec3i; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ClipContext.Fluid; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; @@ -37,20 +38,6 @@ public class BlocksHelper { protected static final BlockState AIR = Blocks.AIR.defaultBlockState(); protected static final BlockState WATER = Blocks.WATER.defaultBlockState(); - private static final Vec3i[] OFFSETS = new Vec3i[] { - new Vec3i(-1, -1, -1), new Vec3i(-1, -1, 0), new Vec3i(-1, -1, 1), - new Vec3i(-1, 0, -1), new Vec3i(-1, 0, 0), new Vec3i(-1, 0, 1), - new Vec3i(-1, 1, -1), new Vec3i(-1, 1, 0), new Vec3i(-1, 1, 1), - - new Vec3i(0, -1, -1), new Vec3i(0, -1, 0), new Vec3i(0, -1, 1), - new Vec3i(0, 0, -1), new Vec3i(0, 0, 0), new Vec3i(0, 0, 1), - new Vec3i(0, 1, -1), new Vec3i(0, 1, 0), new Vec3i(0, 1, 1), - - new Vec3i(1, -1, -1), new Vec3i(1, -1, 0), new Vec3i(1, -1, 1), - new Vec3i(1, 0, -1), new Vec3i(1, 0, 0), new Vec3i(1, 0, 1), - new Vec3i(1, 1, -1), new Vec3i(1, 1, 0), new Vec3i(1, 1, 1) - }; - public static void addBlockColor(Block block, int color) { COLOR_BY_BLOCK.put(block, color); } @@ -107,14 +94,35 @@ public class BlocksHelper { return (int) pos.distSqr(POS); } + /** + * Rotates {@link BlockState} horizontally. Used in block classes with {@link Direction} {@link Property} in rotate function. + * @param state - {@link BlockState} to mirror; + * @param rotation - {@link Rotation}; + * @param facing - Block {@link Direction} {@link Property}. + * @return Rotated {@link BlockState}. + */ public static BlockState rotateHorizontal(BlockState state, Rotation rotation, Property facing) { return state.setValue(facing, rotation.rotate(state.getValue(facing))); } + /** + * Mirrors {@link BlockState} horizontally. Used in block classes with {@link Direction} {@link Property} in mirror function. + * @param state - {@link BlockState} to mirror; + * @param mirror - {@link Mirror}; + * @param facing - Block {@link Direction} {@link Property}. + * @return Mirrored {@link BlockState}. + */ public static BlockState mirrorHorizontal(BlockState state, Mirror mirror, Property facing) { return state.rotate(mirror.getRotation(state.getValue(facing))); } + /** + * Counts the amount of same block down. + * @param world - {@link LevelAccessor} world; + * @param pos - {@link BlockPos} start position; + * @param block - {@link Block} to count. + * @return Integer amount of blocks. + */ public static int getLengthDown(LevelAccessor world, BlockPos pos, Block block) { int count = 1; while (world.getBlockState(pos.below(count)).getBlock() == block) { @@ -123,49 +131,59 @@ public class BlocksHelper { return count; } - public static void cover(LevelAccessor world, BlockPos center, Block ground, BlockState cover, int radius, Random random) { - HashSet points = new HashSet(); - HashSet points2 = new HashSet(); - if (world.getBlockState(center).getBlock() == ground) { - points.add(center); - points2.add(center); - for (int i = 0; i < radius; i++) { - Iterator iterator = points.iterator(); - while (iterator.hasNext()) { - BlockPos pos = iterator.next(); - for (Vec3i offset : OFFSETS) { - if (random.nextBoolean()) { - BlockPos pos2 = pos.offset(offset); - if (random.nextBoolean() && world.getBlockState(pos2).getBlock() == ground - && !points.contains(pos2)) - points2.add(pos2); - } - } - } - points.addAll(points2); - points2.clear(); - } - Iterator iterator = points.iterator(); - while (iterator.hasNext()) { - BlockPos pos = iterator.next(); - BlocksHelper.setWithoutUpdate(world, pos, cover); - } - } - } - + /** + * Creates a new {@link Direction} array with clockwise order: + * NORTH -> EAST -> SOUTH -> WEST + * @return Array of {@link Direction}. + */ public static Direction[] makeHorizontal() { return new Direction[] { Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST }; } + /** + * Get any random horizontal {@link Direction}. + * @param random - {@link Random}. + * @return {@link Direction}. + */ public static Direction randomHorizontal(Random random) { return HORIZONTAL[random.nextInt(4)]; } + /** + * Get any random {@link Direction} including vertical and horizontal. + * @param random - {@link Random}. + * @return {@link Direction}. + */ public static Direction randomDirection(Random random) { return DIRECTIONS[random.nextInt(6)]; } - public static boolean isFluid(BlockState blockState) { - return !blockState.getFluidState().isEmpty(); + /** + * Check if block is {@link Fluid} or not. + * @param state - {@link BlockState} to check. + * @return {@code true} if block is fluid and {@code false} if not. + */ + public static boolean isFluid(BlockState state) { + return !state.getFluidState().isEmpty(); + } + + /** + * Check if block is "invulnerable" like Bedrock. + * @param state - {@link BlockState} to check; + * @param world - {@link BlockGetter} world where BlockState exist; + * @param pos - {@link BlockPos} where BlockState is. + * @return {@code true} if block is "invulnerable" and {@code false} if not. + */ + public static boolean isInvulnerable(BlockState state, BlockGetter world, BlockPos pos) { + return state.getDestroySpeed(world, pos) < 0; + } + + /** + * Check if block is "invulnerable" like Bedrock. Unlike safe function will pass world and position parameters as {@code null}. + * @param state - {@link BlockState} to check. + * @return {@code true} if block is "invulnerable" and {@code false} if not. + */ + public static boolean isInvulnerableUnsafe(BlockState state) { + return isInvulnerable(state, null, null); } } diff --git a/src/main/java/ru/bclib/util/StructureHelper.java b/src/main/java/ru/bclib/util/StructureHelper.java new file mode 100644 index 00000000..f0cc810a --- /dev/null +++ b/src/main/java/ru/bclib/util/StructureHelper.java @@ -0,0 +1,372 @@ +package ru.bclib.util; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Random; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import com.google.common.collect.Sets; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Blocks; +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.levelgen.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; +import net.minecraft.world.level.material.Material; +import ru.bclib.api.BCLibTags; + +public class StructureHelper { + private static final Direction[] DIR = BlocksHelper.makeHorizontal(); + + public static StructureTemplate readStructure(ResourceLocation resource) { + String ns = resource.getNamespace(); + String nm = resource.getPath(); + return readStructure("/data/" + ns + "/structures/" + nm + ".nbt"); + } + + public static StructureTemplate readStructure(File datapack, String path) { + if (datapack.isDirectory()) { + return readStructure(datapack.toString() + "/" + path); + } + else if (datapack.isFile() && datapack.getName().endsWith(".zip")) { + try { + ZipFile zipFile = new ZipFile(datapack); + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String name = entry.getName(); + long compressedSize = entry.getCompressedSize(); + long normalSize = entry.getSize(); + String type = entry.isDirectory() ? "DIR" : "FILE"; + + System.out.println(name); + System.out.format("\t %s - %d - %d\n", type, compressedSize, normalSize); + } + zipFile.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + public static StructureTemplate readStructure(String path) { + try { + InputStream inputstream = StructureHelper.class.getResourceAsStream(path); + 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 static BlockPos offsetPos(BlockPos pos, StructureTemplate structure, Rotation rotation, Mirror mirror) { + BlockPos offset = StructureTemplate.transform(structure.getSize(), mirror, rotation, BlockPos.ZERO); + return pos.offset(-offset.getX() * 0.5, 0, -offset.getZ() * 0.5); + } + + public static void placeCenteredBottom(WorldGenLevel world, BlockPos pos, StructureTemplate structure, Rotation rotation, Mirror mirror, Random random) { + placeCenteredBottom(world, pos, structure, rotation, mirror, makeBox(pos), random); + } + + public static void placeCenteredBottom(WorldGenLevel world, BlockPos pos, StructureTemplate structure, Rotation rotation, Mirror mirror, BoundingBox bounds, Random random) { + BlockPos offset = offsetPos(pos, structure, rotation, mirror); + StructurePlaceSettings placementData = new StructurePlaceSettings().setRotation(rotation).setMirror(mirror).setBoundingBox(bounds); + structure.placeInWorldChunk(world, offset, placementData, random); + } + + private static BoundingBox makeBox(BlockPos pos) { + int sx = ((pos.getX() >> 4) << 4) - 16; + int sz = ((pos.getZ() >> 4) << 4) - 16; + int ex = sx + 47; + int ez = sz + 47; + return BoundingBox.createProper(sx, 0, sz, ex, 255, ez); + } + + public static BoundingBox getStructureBounds(BlockPos pos, StructureTemplate structure, Rotation rotation, Mirror mirror) { + BlockPos max = structure.getSize(); + BlockPos min = StructureTemplate.transform(structure.getSize(), mirror, rotation, BlockPos.ZERO); + max = max.subtract(min); + return new BoundingBox(min.offset(pos), max.offset(pos)); + } + + public static BoundingBox intersectBoxes(BoundingBox box1, BoundingBox box2) { + int x1 = MHelper.max(box1.x0, box2.x0); + int y1 = MHelper.max(box1.y0, box2.y0); + int z1 = MHelper.max(box1.z0, box2.z0); + + int x2 = MHelper.min(box1.x1, box2.x1); + int y2 = MHelper.min(box1.y1, box2.y1); + int z2 = MHelper.min(box1.z1, box2.z1); + + return BoundingBox.createProper(x1, y1, z1, x2, y2, z2); + } + + public static void erode(WorldGenLevel world, BoundingBox bounds, int iterations, Random random) { + MutableBlockPos mut = new MutableBlockPos(); + boolean canDestruct = true; + for (int i = 0; i < iterations; i++) { + for (int x = bounds.x0; x <= bounds.x1; x++) { + mut.setX(x); + for (int z = bounds.z0; z <= bounds.z1; z++) { + mut.setZ(z); + for (int y = bounds.y1; y >= bounds.y0; y--) { + mut.setY(y); + BlockState state = world.getBlockState(mut); + if (canDestruct && BlocksHelper.isInvulnerableUnsafe(state) && random.nextInt(8) == 0 && world.isEmptyBlock(mut.below(2))) { + int r = MHelper.randRange(1, 4, random); + int cx = mut.getX(); + int cy = mut.getY(); + int cz = mut.getZ(); + int x1 = cx - r; + int y1 = cy - r; + int z1 = cz - r; + int x2 = cx + r; + int y2 = cy + r; + int z2 = cz + r; + for (int px = x1; px <= x2; px++) { + int dx = px - cx; + dx *= dx; + mut.setX(px); + for (int py = y1; py <= y2; py++) { + int dy = py - cy; + dy *= dy; + mut.setY(py); + for (int pz = z1; pz <= z2; pz++) { + int dz = pz - cz; + dz *= dz; + mut.setZ(pz); + if (dx + dy + dz <= r && BlocksHelper.isInvulnerableUnsafe(world.getBlockState(mut))) { + BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR); + } + } + } + } + mut.setX(cx); + mut.setY(cy); + mut.setZ(cz); + canDestruct = false; + continue; + } + else if (ignore(state)) { + continue; + } + if (!state.isAir() && random.nextBoolean()) { + MHelper.shuffle(DIR, random); + for (Direction dir: DIR) { + if (world.isEmptyBlock(mut.relative(dir)) && world.isEmptyBlock(mut.below().relative(dir))) { + BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR); + mut.move(dir).move(Direction.DOWN); + for (int py = mut.getY(); y >= bounds.y0 - 10; y--) { + mut.setY(py - 1); + if (!world.isEmptyBlock(mut)) { + mut.setY(py); + BlocksHelper.setWithoutUpdate(world, mut, state); + break; + } + } + } + } + break; + } + else if (random.nextInt(8) == 0 && !BlocksHelper.isInvulnerableUnsafe(world.getBlockState(mut.above()))) { + BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR); + } + } + } + } + } + for (int x = bounds.x0; x <= bounds.x1; x++) { + mut.setX(x); + for (int z = bounds.z0; z <= bounds.z1; z++) { + mut.setZ(z); + for (int y = bounds.y1; y >= bounds.y0; y--) { + mut.setY(y); + BlockState state = world.getBlockState(mut); + if (!ignore(state) && world.isEmptyBlock(mut.below())) { + BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR); + for (int py = mut.getY(); py >= bounds.y0 - 10; py--) { + mut.setY(py - 1); + if (!world.isEmptyBlock(mut)) { + mut.setY(py); + BlocksHelper.setWithoutUpdate(world, mut, state); + break; + } + } + } + } + } + } + } + + public static void erodeIntense(WorldGenLevel world, BoundingBox bounds, Random random) { + MutableBlockPos mut = new MutableBlockPos(); + MutableBlockPos mut2 = new MutableBlockPos(); + int minY = bounds.y0 - 10; + for (int x = bounds.x0; x <= bounds.x1; x++) { + mut.setX(x); + for (int z = bounds.z0; z <= bounds.z1; z++) { + mut.setZ(z); + for (int y = bounds.y1; y >= bounds.y0; y--) { + mut.setY(y); + BlockState state = world.getBlockState(mut); + if (!ignore(state)) { + if (random.nextInt(6) == 0) { + BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR); + if (random.nextBoolean()) { + int px = MHelper.floor(random.nextGaussian() * 2 + x + 0.5); + int pz = MHelper.floor(random.nextGaussian() * 2 + z + 0.5); + mut2.set(px, y, pz); + while (world.getBlockState(mut2).getMaterial().isReplaceable() && mut2.getY() > minY) { + mut2.setY(mut2.getY() - 1); + } + if (!world.getBlockState(mut2).isAir() && state.canSurvive(world, mut2)) { + mut2.setY(mut2.getY() + 1); + BlocksHelper.setWithoutUpdate(world, mut2, state); + } + } + } + else if (random.nextInt(8) == 0) { + BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR); + } + } + } + } + } + + drop(world, bounds); + } + + private static boolean isTerrainNear(WorldGenLevel world, BlockPos pos) { + for (Direction dir: BlocksHelper.DIRECTIONS) { + if (world.getBlockState(pos.relative(dir)).is(BCLibTags.GEN_TERRAIN)) { + return true; + } + } + return false; + } + + private static void drop(WorldGenLevel world, BoundingBox bounds) { + MutableBlockPos mut = new MutableBlockPos(); + + Set blocks = Sets.newHashSet(); + Set edge = Sets.newHashSet(); + Set add = Sets.newHashSet(); + + for (int x = bounds.x0; x <= bounds.x1; x++) { + mut.setX(x); + for (int z = bounds.z0; z <= bounds.z1; z++) { + mut.setZ(z); + for (int y = bounds.y0; y <= bounds.y1; y++) { + mut.setY(y); + BlockState state = world.getBlockState(mut); + if (!ignore(state) && isTerrainNear(world, mut)) { + edge.add(mut.immutable()); + } + } + } + } + + if (edge.isEmpty()) { + return; + } + + while (!edge.isEmpty()) { + for (BlockPos center: edge) { + for (Direction dir: BlocksHelper.DIRECTIONS) { + BlockState state = world.getBlockState(center); + if (state.isCollisionShapeFullBlock(world, center)) { + mut.set(center).move(dir); + if (bounds.isInside(mut)) { + state = world.getBlockState(mut); + if (!ignore(state) && !blocks.contains(mut)) { + add.add(mut.immutable()); + } + } + } + } + } + + blocks.addAll(edge); + edge.clear(); + edge.addAll(add); + add.clear(); + } + + int minY = bounds.y0 - 10; + for (int x = bounds.x0; x <= bounds.x1; x++) { + mut.setX(x); + for (int z = bounds.z0; z <= bounds.z1; z++) { + mut.setZ(z); + for (int y = bounds.y0; y <= bounds.y1; y++) { + mut.setY(y); + BlockState state = world.getBlockState(mut); + if (!ignore(state) && !blocks.contains(mut)) { + BlocksHelper.setWithoutUpdate(world, mut, Blocks.AIR); + while (world.getBlockState(mut).getMaterial().isReplaceable() && mut.getY() > minY) { + mut.setY(mut.getY() - 1); + } + if (mut.getY() > minY) { + mut.setY(mut.getY() + 1); + BlocksHelper.setWithoutUpdate(world, mut, state); + } + } + } + } + } + } + + private static boolean ignore(BlockState state) { + return state.getMaterial().isReplaceable() || + !state.getFluidState().isEmpty() || + state.is(BCLibTags.END_GROUND) || + state.is(BlockTags.LOGS) || + state.is(BlockTags.LEAVES) || + state.getMaterial().equals(Material.PLANT) || + state.getMaterial().equals(Material.LEAVES) || + BlocksHelper.isInvulnerableUnsafe(state); + } + + public static void cover(WorldGenLevel world, BoundingBox bounds, Random random) { + MutableBlockPos mut = new MutableBlockPos(); + for (int x = bounds.x0; x <= bounds.x1; x++) { + mut.setX(x); + for (int z = bounds.z0; z <= bounds.z1; z++) { + mut.setZ(z); + BlockState top = world.getBiome(mut).getGenerationSettings().getSurfaceBuilderConfig().getTopMaterial(); + for (int y = bounds.y1; y >= bounds.y0; y--) { + mut.setY(y); + BlockState state = world.getBlockState(mut); + if (state.is(BCLibTags.END_GROUND) && !world.getBlockState(mut.above()).getMaterial().isSolidBlocking()) { + BlocksHelper.setWithoutUpdate(world, mut, top); + } + } + } + } + } +} diff --git a/src/main/java/ru/bclib/world/biomes/BCLBiome.java b/src/main/java/ru/bclib/world/biomes/BCLBiome.java new file mode 100644 index 00000000..025cdceb --- /dev/null +++ b/src/main/java/ru/bclib/world/biomes/BCLBiome.java @@ -0,0 +1,204 @@ +package ru.bclib.world.biomes; + +import java.io.InputStream; +import java.util.List; +import java.util.Random; + +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.Biome; +import ru.bclib.config.IdConfig; +import ru.bclib.util.JsonFactory; +import ru.bclib.util.StructureHelper; +import ru.bclib.world.features.BCLFeature; +import ru.bclib.world.features.ListFeature; +import ru.bclib.world.features.ListFeature.StructureInfo; +import ru.bclib.world.features.NBTStructureFeature.TerrainMerge; + +public class BCLBiome { + protected List subbiomes = Lists.newArrayList(); + + protected final Biome biome; + protected final ResourceLocation mcID; + protected BCLBiome edge; + protected int edgeSize; + + protected BCLBiome biomeParent; + protected float maxSubBiomeChance = 1; + protected final float genChanceUnmutable; + protected float genChance = 1; + + private final float fogDensity; + private BCLFeature structuresFeature; + private Biome actualBiome; + + public BCLBiome(BiomeDefinition definition, IdConfig config) { + this.mcID = definition.getID(); + this.readStructureList(); + if (structuresFeature != null) { + definition.addFeature(structuresFeature); + } + this.biome = definition.build(); + this.fogDensity = config.getFloat(mcID, "fog_density", definition.getFodDensity()); + this.genChanceUnmutable = config.getFloat(mcID, "generation_chance", definition.getGenChance()); + this.edgeSize = config.getInt(mcID, "edge_size", 32); + } + + public BCLBiome(ResourceLocation id, Biome biome, float fogDensity, float genChance, boolean hasCaves, IdConfig config) { + this.mcID = id; + this.readStructureList(); + this.biome = biome; + this.fogDensity = config.getFloat(mcID, "fog_density", fogDensity); + this.genChanceUnmutable = config.getFloat(mcID, "generation_chance", genChance); + this.edgeSize = config.getInt(mcID, "edge_size", 32); + } + + public BCLBiome getEdge() { + return edge == null ? this : edge; + } + + public void setEdge(BCLBiome edge) { + this.edge = edge; + edge.biomeParent = this; + } + + public int getEdgeSize() { + return edgeSize; + } + + public void setEdgeSize(int size) { + edgeSize = size; + } + + public void addSubBiome(BCLBiome biome) { + maxSubBiomeChance += biome.mutateGenChance(maxSubBiomeChance); + biome.biomeParent = this; + subbiomes.add(biome); + } + + public boolean containsSubBiome(BCLBiome biome) { + return subbiomes.contains(biome); + } + + public BCLBiome getSubBiome(Random random) { + float chance = random.nextFloat() * maxSubBiomeChance; + for (BCLBiome biome : subbiomes) + if (biome.canGenerate(chance)) + return biome; + return this; + } + + public BCLBiome getParentBiome() { + return this.biomeParent; + } + + public boolean hasEdge() { + return edge != null; + } + + public boolean hasParentBiome() { + return biomeParent != null; + } + + public boolean isSame(BCLBiome biome) { + return biome == this || (biome.hasParentBiome() && biome.getParentBiome() == this); + } + + public boolean canGenerate(float chance) { + return chance <= this.genChance; + } + + public float mutateGenChance(float chance) { + genChance = genChanceUnmutable; + genChance += chance; + return genChance; + } + + public Biome getBiome() { + return biome; + } + + @Override + public String toString() { + return mcID.toString(); + } + + public ResourceLocation getID() { + return mcID; + } + + public float getFogDensity() { + return fogDensity; + } + + protected void readStructureList() { + String ns = mcID.getNamespace(); + String nm = mcID.getPath(); + + String path = "/data/" + ns + "/structures/biome/" + nm + "/"; + InputStream inputstream = StructureHelper.class.getResourceAsStream(path + "structures.json"); + if (inputstream != null) { + JsonObject obj = JsonFactory.getJsonObject(inputstream); + JsonArray enties = obj.getAsJsonArray("structures"); + if (enties != null) { + List list = Lists.newArrayList(); + enties.forEach((entry) -> { + JsonObject e = entry.getAsJsonObject(); + String structure = path + e.get("nbt").getAsString() + ".nbt"; + TerrainMerge terrainMerge = TerrainMerge.getFromString(e.get("terrainMerge").getAsString()); + int offsetY = e.get("offsetY").getAsInt(); + list.add(new StructureInfo(structure, offsetY, terrainMerge)); + }); + if (!list.isEmpty()) { + structuresFeature = BCLFeature.makeChansedFeature(new ResourceLocation(ns, nm + "_structures"), new ListFeature(list), 10); + } + } + } + } + + public BCLFeature getStructuresFeature() { + return structuresFeature; + } + + public Biome getActualBiome() { + return this.actualBiome; + } + + public float getGenChance() { + return this.genChance; + } + + public float getGenChanceImmutable() { + return this.genChanceUnmutable; + } + + public void updateActualBiomes(Registry biomeRegistry) { + subbiomes.forEach((sub) -> { + if (sub != this) { + sub.updateActualBiomes(biomeRegistry); + } + }); + if (edge != null && edge != this) { + edge.updateActualBiomes(biomeRegistry); + } + this.actualBiome = biomeRegistry.get(mcID); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + BCLBiome biome = (BCLBiome) obj; + return biome == null ? false : biome.mcID.equals(mcID); + } + + @Override + public int hashCode() { + return mcID.hashCode(); + } +} diff --git a/src/main/java/ru/bclib/world/biomes/BiomeDefinition.java b/src/main/java/ru/bclib/world/biomes/BiomeDefinition.java new file mode 100644 index 00000000..ffa3075d --- /dev/null +++ b/src/main/java/ru/bclib/world/biomes/BiomeDefinition.java @@ -0,0 +1,358 @@ +package ru.bclib.world.biomes; + +import java.util.List; + +import com.google.common.collect.Lists; + +import net.minecraft.core.Registry; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.data.worldgen.biome.Biomes; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.Music; +import net.minecraft.sounds.Musics; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.biome.AmbientAdditionsSettings; +import net.minecraft.world.level.biome.AmbientMoodSettings; +import net.minecraft.world.level.biome.AmbientParticleSettings; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biome.BiomeCategory; +import net.minecraft.world.level.biome.Biome.Precipitation; +import net.minecraft.world.level.biome.BiomeGenerationSettings; +import net.minecraft.world.level.biome.BiomeSpecialEffects.Builder; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.levelgen.GenerationStep.Carving; +import net.minecraft.world.level.levelgen.GenerationStep.Decoration; +import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature; +import net.minecraft.world.level.levelgen.feature.configurations.ProbabilityFeatureConfiguration; +import net.minecraft.world.level.levelgen.surfacebuilders.ConfiguredSurfaceBuilder; +import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilder; +import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilderBaseConfiguration; +import ru.bclib.util.ColorUtil; +import ru.bclib.world.features.BCLFeature; +import ru.bclib.world.structures.BCLStructureFeature; +import ru.bclib.world.surface.DoubleBlockSurfaceBuilder; + +public class BiomeDefinition { + private static final int DEF_FOLIAGE_OVERWORLD = Biomes.PLAINS.getFoliageColor(); + private static final int DEF_FOLIAGE_NETHER =ColorUtil.color(117, 10, 10); + private static final int DEF_FOLIAGE_END = ColorUtil.color(197, 210, 112); + + private final List> structures = Lists.newArrayList(); + private final List features = Lists.newArrayList(); + private final List carvers = Lists.newArrayList(); + private final List mobs = Lists.newArrayList(); + private final List spawns = Lists.newArrayList(); + + private final ResourceLocation id; + + private AmbientParticleSettings particleConfig; + private AmbientAdditionsSettings additions; + private AmbientMoodSettings mood; + private SoundEvent music; + private SoundEvent loop; + + private int foliageColor = DEF_FOLIAGE_OVERWORLD; + private int grassColor = DEF_FOLIAGE_OVERWORLD; + private int waterFogColor = 329011; + private int waterColor = 4159204; + private int fogColor = 10518688; + private float fogDensity = 1F; + private float depth = 0.1F; + + private Precipitation precipitation = Precipitation.NONE; + private BiomeCategory category = BiomeCategory.NONE; + private float temperature = 1F; + private float genChance = 1F; + private float downfall = 0F; + private int edgeSize = 32; + + private ConfiguredSurfaceBuilder surface; + + /** + * Custom biome definition. Can be extended with new parameters. + * @param id - Biome {@link ResourceLocation} (identifier). + */ + public BiomeDefinition(ResourceLocation id) { + this.id = id; + } + + /** + * Create default definition for The Nether biome. + * @param id + * @return + */ + public static BiomeDefinition netherBiome(ResourceLocation id) { + BiomeDefinition def = new BiomeDefinition(id); + def.foliageColor = DEF_FOLIAGE_NETHER; + def.grassColor = DEF_FOLIAGE_NETHER; + def.setCategory(BiomeCategory.NETHER); + return def; + } + + /** + * Create default definition for The End biome. + * @param id + * @return + */ + public static BiomeDefinition endBiome(ResourceLocation id) { + BiomeDefinition def = new BiomeDefinition(id); + def.foliageColor = DEF_FOLIAGE_END; + def.grassColor = DEF_FOLIAGE_END; + def.setCategory(BiomeCategory.THEEND); + return def; + } + + public BiomeDefinition setCategory(BiomeCategory category) { + this.category = category; + return this; + } + + public BiomeDefinition setPrecipitation(Precipitation precipitation) { + this.precipitation = precipitation; + return this; + } + + public BiomeDefinition setSurface(Block block) { + setSurface(SurfaceBuilder.DEFAULT.configured(new SurfaceBuilderBaseConfiguration( + block.defaultBlockState(), + Blocks.END_STONE.defaultBlockState(), + Blocks.END_STONE.defaultBlockState() + ))); + return this; + } + + public BiomeDefinition setSurface(Block block1, Block block2) { + setSurface(DoubleBlockSurfaceBuilder.register("bclib_" + id.getPath() + "_surface").setBlock1(block1).setBlock2(block2).configured()); + return this; + } + + public BiomeDefinition setSurface(ConfiguredSurfaceBuilder builder) { + this.surface = builder; + return this; + } + + public BiomeDefinition setParticles(ParticleOptions particle, float probability) { + this.particleConfig = new AmbientParticleSettings(particle, probability); + return this; + } + + public BiomeDefinition setGenChance(float genChance) { + this.genChance = genChance; + return this; + } + + public BiomeDefinition setDepth(float depth) { + this.depth = depth; + return this; + } + + public BiomeDefinition setTemperature(float temperature) { + this.temperature = temperature; + return this; + } + + public BiomeDefinition setDownfall(float downfall) { + this.downfall = downfall; + return this; + } + + public BiomeDefinition setEdgeSize(int edgeSize) { + this.edgeSize = edgeSize; + return this; + } + + public BiomeDefinition addMobSpawn(EntityType type, int weight, int minGroupSize, int maxGroupSize) { + ResourceLocation eID = Registry.ENTITY_TYPE.getKey(type); + if (eID != Registry.ENTITY_TYPE.getDefaultKey()) { + SpawnInfo info = new SpawnInfo(); + info.type = type; + info.weight = weight; + info.minGroupSize = minGroupSize; + info.maxGroupSize = maxGroupSize; + mobs.add(info); + } + return this; + } + + public BiomeDefinition addMobSpawn(SpawnerData entry) { + spawns.add(entry); + return this; + } + + public BiomeDefinition addStructureFeature(ConfiguredStructureFeature feature) { + structures.add(feature); + return this; + } + + public BiomeDefinition addStructureFeature(BCLStructureFeature feature) { + structures.add(feature.getFeatureConfigured()); + return this; + } + + public BiomeDefinition addFeature(BCLFeature feature) { + FeatureInfo info = new FeatureInfo(); + info.featureStep = feature.getFeatureStep(); + info.feature = feature.getFeatureConfigured(); + features.add(info); + return this; + } + + public BiomeDefinition addFeature(Decoration featureStep, ConfiguredFeature feature) { + FeatureInfo info = new FeatureInfo(); + info.featureStep = featureStep; + info.feature = feature; + features.add(info); + return this; + } + + private int getColor(int r, int g, int b) { + r = Mth.clamp(r, 0, 255); + g = Mth.clamp(g, 0, 255); + b = Mth.clamp(b, 0, 255); + return ColorUtil.color(r, g, b); + } + + public BiomeDefinition setFogColor(int r, int g, int b) { + this.fogColor = getColor(r, g, b); + return this; + } + + public BiomeDefinition setFogDensity(float density) { + this.fogDensity = density; + return this; + } + + public BiomeDefinition setWaterColor(int r, int g, int b) { + this.waterColor = getColor(r, g, b); + return this; + } + + public BiomeDefinition setWaterFogColor(int r, int g, int b) { + this.waterFogColor = getColor(r, g, b); + return this; + } + + public BiomeDefinition setWaterAndFogColor(int r, int g, int b) { + return setWaterColor(r, g, b).setWaterFogColor(r, g, b); + } + + public BiomeDefinition setFoliageColor(int r, int g, int b) { + this.foliageColor = getColor(r, g, b); + return this; + } + + public BiomeDefinition setGrassColor(int r, int g, int b) { + this.grassColor = getColor(r, g, b); + return this; + } + + public BiomeDefinition setPlantsColor(int r, int g, int b) { + return this.setFoliageColor(r, g, b).setGrassColor(r, g, b); + } + + public BiomeDefinition setLoop(SoundEvent loop) { + this.loop = loop; + return this; + } + + public BiomeDefinition setMood(SoundEvent mood) { + this.mood = new AmbientMoodSettings(mood, 6000, 8, 2.0D); + return this; + } + + public BiomeDefinition setAdditions(SoundEvent additions) { + this.additions = new AmbientAdditionsSettings(additions, 0.0111); + return this; + } + + public BiomeDefinition setMusic(SoundEvent music) { + this.music = music; + return this; + } + + public Biome build() { + MobSpawnSettings.Builder spawnSettings = new MobSpawnSettings.Builder(); + BiomeGenerationSettings.Builder generationSettings = new BiomeGenerationSettings.Builder(); + Builder effects = new Builder(); + + mobs.forEach((spawn) -> { + spawnSettings.addSpawn(spawn.type.getCategory(), new MobSpawnSettings.SpawnerData(spawn.type, spawn.weight, spawn.minGroupSize, spawn.maxGroupSize)); + }); + + spawns.forEach((entry) -> { + spawnSettings.addSpawn(entry.type.getCategory(), entry); + }); + + generationSettings.surfaceBuilder(surface == null ? net.minecraft.data.worldgen.SurfaceBuilders.END : surface); + structures.forEach((structure) -> generationSettings.addStructureStart(structure)); + features.forEach((info) -> generationSettings.addFeature(info.featureStep, info.feature)); + carvers.forEach((info) -> generationSettings.addCarver(info.carverStep, info.carver)); + + effects.skyColor(0).waterColor(waterColor).waterFogColor(waterFogColor).fogColor(fogColor).foliageColorOverride(foliageColor).grassColorOverride(grassColor); + if (loop != null) effects.ambientLoopSound(loop); + if (mood != null) effects.ambientMoodSound(mood); + if (additions != null) effects.ambientAdditionsSound(additions); + if (particleConfig != null) effects.ambientParticle(particleConfig); + effects.backgroundMusic(music != null ? new Music(music, 600, 2400, true) : Musics.END); + + return new Biome.BiomeBuilder() + .precipitation(precipitation) + .biomeCategory(category) + .depth(depth) + .scale(0.2F) + .temperature(temperature) + .downfall(downfall) + .specialEffects(effects.build()) + .mobSpawnSettings(spawnSettings.build()) + .generationSettings(generationSettings.build()) + .build(); + } + + private static final class SpawnInfo { + EntityType type; + int weight; + int minGroupSize; + int maxGroupSize; + } + + private static final class FeatureInfo { + Decoration featureStep; + ConfiguredFeature feature; + } + + private static final class CarverInfo { + Carving carverStep; + ConfiguredWorldCarver carver; + } + + public ResourceLocation getID() { + return id; + } + + public float getFodDensity() { + return fogDensity; + } + + public float getGenChance() { + return genChance; + } + + public int getEdgeSize() { + return edgeSize; + } + + public BiomeDefinition addCarver(Carving carverStep, ConfiguredWorldCarver carver) { + CarverInfo info = new CarverInfo(); + info.carverStep = carverStep; + info.carver = carver; + carvers.add(info); + return this; + } +} \ No newline at end of file diff --git a/src/main/java/ru/bclib/world/features/BCLFeature.java b/src/main/java/ru/bclib/world/features/BCLFeature.java new file mode 100644 index 00000000..d8d3483e --- /dev/null +++ b/src/main/java/ru/bclib/world/features/BCLFeature.java @@ -0,0 +1,94 @@ +package ru.bclib.world.features; + +import net.minecraft.core.Registry; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.data.worldgen.Features; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.CountConfiguration; +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; +import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; +import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguration; +import net.minecraft.world.level.levelgen.feature.configurations.RangeDecoratorConfiguration; +import net.minecraft.world.level.levelgen.placement.ChanceDecoratorConfiguration; +import net.minecraft.world.level.levelgen.placement.FeatureDecorator; +import net.minecraft.world.level.levelgen.structure.templatesystem.BlockMatchTest; + +public class BCLFeature { + private Feature feature; + private ConfiguredFeature featureConfigured; + private GenerationStep.Decoration featureStep; + + public BCLFeature(Feature feature, ConfiguredFeature configuredFeature, GenerationStep.Decoration featureStep) { + this.featureConfigured = configuredFeature; + this.featureStep = featureStep; + this.feature = feature; + } + + public BCLFeature(ResourceLocation id, Feature feature, GenerationStep.Decoration featureStep, ConfiguredFeature configuredFeature) { + this.featureConfigured = Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, id, configuredFeature); + this.feature = Registry.register(Registry.FEATURE, id, feature); + this.featureStep = featureStep; + } + + public static BCLFeature makeVegetationFeature(ResourceLocation id, Feature feature, int density) { + ConfiguredFeature configured = feature.configured(FeatureConfiguration.NONE).decorated(Features.Decorators.HEIGHTMAP_SQUARE).countRandom(density); + return new BCLFeature(id, feature, GenerationStep.Decoration.VEGETAL_DECORATION, configured); + } + + public static BCLFeature makeRawGenFeature(ResourceLocation id, Feature feature, int chance) { + ConfiguredFeature configured = feature.configured(FeatureConfiguration.NONE).decorated(FeatureDecorator.CHANCE.configured(new ChanceDecoratorConfiguration(chance))); + return new BCLFeature(id, feature, GenerationStep.Decoration.RAW_GENERATION, configured); + } + + public static BCLFeature makeLakeFeature(ResourceLocation id, Feature feature, int chance) { + ConfiguredFeature configured = feature.configured(FeatureConfiguration.NONE).decorated(FeatureDecorator.WATER_LAKE.configured(new ChanceDecoratorConfiguration(chance))); + return new BCLFeature(id, feature, GenerationStep.Decoration.LAKES, configured); + } + + public static BCLFeature makeOreFeature(ResourceLocation id, Block blockOre, int veins, int veinSize, int offset, int minY, int maxY) { + OreConfiguration featureConfig = new OreConfiguration(new BlockMatchTest(Blocks.END_STONE), blockOre.defaultBlockState(), veinSize); + RangeDecoratorConfiguration rangeDecorator = new RangeDecoratorConfiguration(offset, minY, maxY); + ConfiguredFeature oreFeature = Feature.ORE.configured(featureConfig) + .decorated(FeatureDecorator.RANGE.configured(rangeDecorator)) + .squared() + .count(veins); + return new BCLFeature(Feature.ORE, Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, id, oreFeature), GenerationStep.Decoration.UNDERGROUND_ORES); + } + + public static BCLFeature makeChunkFeature(ResourceLocation id, Feature feature) { + ConfiguredFeature configured = feature.configured(FeatureConfiguration.NONE).decorated(FeatureDecorator.COUNT.configured(new CountConfiguration(1))); + return new BCLFeature(id, feature, GenerationStep.Decoration.LOCAL_MODIFICATIONS, configured); + } + + public static BCLFeature makeChansedFeature(ResourceLocation id, Feature feature, int chance) { + ConfiguredFeature configured = feature.configured(FeatureConfiguration.NONE).decorated(FeatureDecorator.CHANCE.configured(new ChanceDecoratorConfiguration(chance))); + return new BCLFeature(id, feature, GenerationStep.Decoration.SURFACE_STRUCTURES, configured); + } + + public static BCLFeature makeCountRawFeature(ResourceLocation id, Feature feature, int chance) { + ConfiguredFeature configured = feature.configured(FeatureConfiguration.NONE).decorated(FeatureDecorator.COUNT.configured(new CountConfiguration(chance))); + return new BCLFeature(id, feature, GenerationStep.Decoration.RAW_GENERATION, configured); + } + + public static BCLFeature makeFeatureConfigured(ResourceLocation id, Feature feature) { + ConfiguredFeature configured = feature.configured(FeatureConfiguration.NONE); + return new BCLFeature(id, feature, GenerationStep.Decoration.RAW_GENERATION, configured); + } + + public Feature getFeature() { + return feature; + } + + public ConfiguredFeature getFeatureConfigured() { + return featureConfigured; + } + + public GenerationStep.Decoration getFeatureStep() { + return featureStep; + } +} diff --git a/src/main/java/ru/bclib/world/features/DefaultFeature.java b/src/main/java/ru/bclib/world/features/DefaultFeature.java new file mode 100644 index 00000000..30553274 --- /dev/null +++ b/src/main/java/ru/bclib/world/features/DefaultFeature.java @@ -0,0 +1,44 @@ +package ru.bclib.world.features; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.Heightmap.Types; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration; +import ru.bclib.util.BlocksHelper; + +public abstract class DefaultFeature extends Feature { + protected static final BlockState AIR = Blocks.AIR.defaultBlockState(); + protected static final BlockState WATER = Blocks.WATER.defaultBlockState(); + + public DefaultFeature() { + super(NoneFeatureConfiguration.CODEC); + } + + public static int getYOnSurface(WorldGenLevel world, int x, int z) { + return world.getHeight(Types.WORLD_SURFACE, x, z); + } + + public static int getYOnSurfaceWG(WorldGenLevel world, int x, int z) { + return world.getHeight(Types.WORLD_SURFACE_WG, x, z); + } + + public static BlockPos getPosOnSurface(WorldGenLevel world, BlockPos pos) { + return world.getHeightmapPos(Types.WORLD_SURFACE, pos); + } + + public static BlockPos getPosOnSurfaceWG(WorldGenLevel world, BlockPos pos) { + return world.getHeightmapPos(Types.WORLD_SURFACE_WG, pos); + } + + public static BlockPos getPosOnSurfaceRaycast(WorldGenLevel world, BlockPos pos) { + return getPosOnSurfaceRaycast(world, pos, 256); + } + + public static BlockPos getPosOnSurfaceRaycast(WorldGenLevel world, BlockPos pos, int dist) { + int h = BlocksHelper.downRay(world, pos, dist); + return pos.below(h); + } +} diff --git a/src/main/java/ru/bclib/world/features/ListFeature.java b/src/main/java/ru/bclib/world/features/ListFeature.java new file mode 100644 index 00000000..8f00c087 --- /dev/null +++ b/src/main/java/ru/bclib/world/features/ListFeature.java @@ -0,0 +1,78 @@ +package ru.bclib.world.features; + +import java.util.List; +import java.util.Random; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; +import ru.bclib.util.StructureHelper; + +public class ListFeature extends NBTStructureFeature { + private final List list; + private StructureInfo selected; + + public ListFeature(List list) { + this.list = list; + } + + @Override + protected StructureTemplate getStructure(WorldGenLevel world, BlockPos pos, Random random) { + selected = list.get(random.nextInt(list.size())); + return selected.getStructure(); + } + + @Override + protected boolean canSpawn(WorldGenLevel world, BlockPos pos, Random random) { + int cx = pos.getX() >> 4; + int cz = pos.getZ() >> 4; + return ((cx + cz) & 1) == 0 && pos.getY() > 58;// && world.getBlockState(pos.below()).is(EndTags.GEN_TERRAIN); + } + + @Override + protected Rotation getRotation(WorldGenLevel world, BlockPos pos, Random random) { + return Rotation.getRandom(random); + } + + @Override + protected Mirror getMirror(WorldGenLevel world, BlockPos pos, Random random) { + return Mirror.values()[random.nextInt(3)]; + } + + @Override + protected int getYOffset(StructureTemplate structure, WorldGenLevel world, BlockPos pos, Random random) { + return selected.offsetY; + } + + @Override + protected TerrainMerge getTerrainMerge(WorldGenLevel world, BlockPos pos, Random random) { + return selected.terrainMerge; + } + + @Override + protected void addStructureData(StructurePlaceSettings data) {} + + public static final class StructureInfo { + public final TerrainMerge terrainMerge; + public final String structurePath; + public final int offsetY; + + private StructureTemplate structure; + + public StructureInfo(String structurePath, int offsetY, TerrainMerge terrainMerge) { + this.terrainMerge = terrainMerge; + this.structurePath = structurePath; + this.offsetY = offsetY; + } + + public StructureTemplate getStructure() { + if (structure == null) { + structure = StructureHelper.readStructure(structurePath); + } + return structure; + } + } +} diff --git a/src/main/java/ru/bclib/world/processors/DestructionStructureProcessor.java b/src/main/java/ru/bclib/world/processors/DestructionStructureProcessor.java new file mode 100644 index 00000000..3d12057a --- /dev/null +++ b/src/main/java/ru/bclib/world/processors/DestructionStructureProcessor.java @@ -0,0 +1,31 @@ +package ru.bclib.world.processors; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; +import ru.bclib.util.BlocksHelper; +import ru.bclib.util.MHelper; + +public class DestructionStructureProcessor extends StructureProcessor { + private int chance = 4; + + public void setChance(int chance) { + this.chance = chance; + } + + @Override + public StructureBlockInfo processBlock(LevelReader worldView, BlockPos pos, BlockPos blockPos, StructureBlockInfo structureBlockInfo, StructureBlockInfo structureBlockInfo2, StructurePlaceSettings structurePlacementData) { + if (!BlocksHelper.isInvulnerable(structureBlockInfo2.state, worldView, structureBlockInfo2.pos) && MHelper.RANDOM.nextInt(chance) == 0) { + return null; + } + return structureBlockInfo2; + } + + @Override + protected StructureProcessorType getType() { + return StructureProcessorType.RULE; + } +} diff --git a/src/main/java/ru/bclib/world/processors/TerrainStructureProcessor.java b/src/main/java/ru/bclib/world/processors/TerrainStructureProcessor.java new file mode 100644 index 00000000..dceb0ee1 --- /dev/null +++ b/src/main/java/ru/bclib/world/processors/TerrainStructureProcessor.java @@ -0,0 +1,27 @@ +package ru.bclib.world.processors; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; + +public class TerrainStructureProcessor extends StructureProcessor { + @Override + public StructureBlockInfo processBlock(LevelReader worldView, BlockPos pos, BlockPos blockPos, StructureBlockInfo structureBlockInfo, StructureBlockInfo structureBlockInfo2, StructurePlaceSettings structurePlacementData) { + BlockPos bpos = structureBlockInfo2.pos; + if (structureBlockInfo2.state.is(Blocks.END_STONE) && worldView.isEmptyBlock(bpos.above())) { + BlockState top = worldView.getBiome(structureBlockInfo2.pos).getGenerationSettings().getSurfaceBuilderConfig().getTopMaterial(); + return new StructureBlockInfo(bpos, top, structureBlockInfo2.nbt); + } + return structureBlockInfo2; + } + + @Override + protected StructureProcessorType getType() { + return StructureProcessorType.RULE; + } +} diff --git a/src/main/java/ru/bclib/world/surface/DoubleBlockSurfaceBuilder.java b/src/main/java/ru/bclib/world/surface/DoubleBlockSurfaceBuilder.java new file mode 100644 index 00000000..1e19d33e --- /dev/null +++ b/src/main/java/ru/bclib/world/surface/DoubleBlockSurfaceBuilder.java @@ -0,0 +1,52 @@ +package ru.bclib.world.surface; + +import java.util.Random; + +import net.minecraft.core.Registry; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.levelgen.surfacebuilders.ConfiguredSurfaceBuilder; +import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilder; +import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilderBaseConfiguration; +import ru.bclib.noise.OpenSimplexNoise; +import ru.bclib.util.MHelper; + +public class DoubleBlockSurfaceBuilder extends SurfaceBuilder { + private static final OpenSimplexNoise NOISE = new OpenSimplexNoise(4141); + private SurfaceBuilderBaseConfiguration config1; + private SurfaceBuilderBaseConfiguration config2; + + private DoubleBlockSurfaceBuilder() { + super(SurfaceBuilderBaseConfiguration.CODEC); + } + + public DoubleBlockSurfaceBuilder setBlock1(Block block) { + BlockState stone = Blocks.END_STONE.defaultBlockState(); + config1 = new SurfaceBuilderBaseConfiguration(block.defaultBlockState(), stone, stone); + return this; + } + + public DoubleBlockSurfaceBuilder setBlock2(Block block) { + BlockState stone = Blocks.END_STONE.defaultBlockState(); + config2 = new SurfaceBuilderBaseConfiguration(block.defaultBlockState(), stone, stone); + return this; + } + + @Override + public void apply(Random random, ChunkAccess chunk, Biome biome, int x, int z, int height, double noise, BlockState defaultBlock, BlockState defaultFluid, int seaLevel, long seed, SurfaceBuilderBaseConfiguration surfaceBlocks) { + noise = NOISE.eval(x * 0.1, z * 0.1) + MHelper.randRange(-0.4, 0.4, random); + SurfaceBuilder.DEFAULT.apply(random, chunk, biome, x, z, height, noise, defaultBlock, defaultFluid, seaLevel, seed, noise > 0 ? config1 : config2); + } + + public static DoubleBlockSurfaceBuilder register(String name) { + return Registry.register(Registry.SURFACE_BUILDER, name, new DoubleBlockSurfaceBuilder()); + } + + public ConfiguredSurfaceBuilder configured() { + BlockState stone = Blocks.END_STONE.defaultBlockState(); + return this.configured(new SurfaceBuilderBaseConfiguration(config1.getTopMaterial(), stone, stone)); + } +} \ No newline at end of file diff --git a/src/main/resources/bclib.mixins.common.json b/src/main/resources/bclib.mixins.common.json index 64ca25bb..f88b1ac2 100644 --- a/src/main/resources/bclib.mixins.common.json +++ b/src/main/resources/bclib.mixins.common.json @@ -3,7 +3,9 @@ "minVersion": "0.8", "package": "ru.bclib.mixin.common", "compatibilityLevel": "JAVA_8", - "mixins": [], + "mixins": [ + "TagLoaderMixin" + ], "injectors": { "defaultRequire": 1 }