diff --git a/.gitignore b/.gitignore index 10a748a9..fc4c4dbf 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ dependency-reduced-pom.xml .gradle build libs/ +paper/run diff --git a/paper/src/main/java/com/untamedears/realisticbiomes/RBConfigManager.java b/paper/src/main/java/com/untamedears/realisticbiomes/RBConfigManager.java index 20ce32aa..1e45cbe7 100644 --- a/paper/src/main/java/com/untamedears/realisticbiomes/RBConfigManager.java +++ b/paper/src/main/java/com/untamedears/realisticbiomes/RBConfigManager.java @@ -11,6 +11,7 @@ import com.untamedears.realisticbiomes.growth.HorizontalBlockSpreadGrower; import com.untamedears.realisticbiomes.growth.IArtificialGrower; import com.untamedears.realisticbiomes.growth.KelpGrower; +import com.untamedears.realisticbiomes.growth.NMSFeatureGrower; import com.untamedears.realisticbiomes.growth.SchematicGrower; import com.untamedears.realisticbiomes.growth.SeaPickleGrower; import com.untamedears.realisticbiomes.growth.StemGrower; @@ -306,6 +307,8 @@ private IArtificialGrower parseGrower(ConfigurationSection section, ItemStack it return new StemGrower(material, fruitConfig); case "tree": return new TreeGrower(material); + case "nmstree": + return new NMSFeatureGrower(material); case "horizontalspread": int maxAmount = section.getInt("max_amount"); int horRange = section.getInt("max_range"); diff --git a/paper/src/main/java/com/untamedears/realisticbiomes/growth/NMSFeatureGrower.java b/paper/src/main/java/com/untamedears/realisticbiomes/growth/NMSFeatureGrower.java new file mode 100644 index 00000000..d266f92e --- /dev/null +++ b/paper/src/main/java/com/untamedears/realisticbiomes/growth/NMSFeatureGrower.java @@ -0,0 +1,188 @@ +package com.untamedears.realisticbiomes.growth; + +import java.util.Iterator; +import java.util.Random; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.data.type.Sapling; +import org.bukkit.craftbukkit.v1_18_R2.block.CraftBlock; + +import com.untamedears.realisticbiomes.model.Plant; + +import net.minecraft.core.BlockPos; +import net.minecraft.data.worldgen.features.TreeFeatures; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.grower.AbstractMegaTreeGrower; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; + +public class NMSFeatureGrower extends AgeableGrower { + + public NMSFeatureGrower(Material material) { + super(material, 1, 1); + } + + private boolean isMega; + + + + + @Override + public int getStage(Plant plant) { + Block block = plant.getLocation().getBlock(); + if (block.getType() != this.material) { + return -1; + } + return 0; + } + + @Override + public boolean setStage(Plant plant, int stage) { + if (stage < 1) { + return true; + } + Block block = plant.getLocation().getBlock(); + // Re-Read the block data to make sure it is up to date + if (!(block.getBlockData() instanceof Sapling||block.getBlockData().getMaterial() == Material.FLOWERING_AZALEA)) { + return true; + } + + + CraftBlock cBlock = (CraftBlock) block; + BlockPos pos = cBlock.getPosition(); + ServerLevel spWorld = cBlock.getCraftWorld().getHandle(); + BlockState spBlock = cBlock.getNMS(); + + this.growTreeFeature(spWorld, spWorld.getChunkSource().getGenerator(), pos, spBlock, new Random()); + return true; + } + + public boolean growTreeFeature( + ServerLevel world, + ChunkGenerator chunkGenerator, + BlockPos pos, + BlockState state, + Random random) { + isMega = false; + if(placeMegaTree(world, chunkGenerator, pos, state, random)) {return true;}; + if(isMega) return false; // fail tree growth here if it's a mega tree and it hasn't returned true above + ConfiguredFeature feature; + ConfiguredFeature featureSmall; + //feature holds the default Minecraft tree and featureSmall holds a version with randomHeight set to 0 so that it will grow at minimum height + switch(state.getBukkitMaterial()) { + case JUNGLE_SAPLING: + feature = TreeFeatures.JUNGLE_TREE_NO_VINE.value(); + featureSmall = RBFeatures.SHORT_JUNGLE.value(); + break; + case SPRUCE_SAPLING: + feature = TreeFeatures.SPRUCE.value(); + featureSmall = RBFeatures.SHORT_SPRUCE.value(); + break; + case OAK_SAPLING: + feature = this.hasFlowers(world, pos) ? TreeFeatures.OAK_BEES_005.value() : TreeFeatures.OAK.value(); + featureSmall = this.hasFlowers(world, pos) ? RBFeatures.SHORT_OAK_BEES.value() : RBFeatures.SHORT_OAK.value(); + break; + case BIRCH_SAPLING: + feature = this.hasFlowers(world, pos) ? TreeFeatures.BIRCH_BEES_005.value() : TreeFeatures.BIRCH.value(); + featureSmall = this.hasFlowers(world, pos) ? RBFeatures.SHORT_BIRCH_BEES.value() : RBFeatures.SHORT_BIRCH.value(); + break; + case ACACIA_SAPLING: + feature = TreeFeatures.ACACIA.value(); + featureSmall = RBFeatures.SHORT_ACACIA.value(); + break; + case FLOWERING_AZALEA: + feature = TreeFeatures.AZALEA_TREE.value(); + featureSmall = RBFeatures.SHORT_AZALEA_TREE.value(); + break; + case DARK_OAK_SAPLING: //completly unecessary only left this in for the sake of completion tbh + default: + return false; + } + world.setBlock(pos, Blocks.AIR.defaultBlockState(), 4); + if (feature.place(world, chunkGenerator, random, pos)) { //attempt to place the normal tree feature + return true; + } else if(featureSmall.place(world, chunkGenerator, random, pos)) { //attempt to place the small/short version if the normal fails + return true; + } else { + world.setBlock(pos, state, 4); //replace the sappling if both fail + return false; + } + } + + private boolean placeMegaTree(ServerLevel world, ChunkGenerator chunkGenerator, BlockPos pos, BlockState state, Random random) { + ConfiguredFeature feature; + ConfiguredFeature featureSmall; + //feature holds the default minecraft tree and featuresmall holds a version with randomHeight set to 0 so that it will grow at minimum height + switch(state.getBukkitMaterial()) { + case JUNGLE_SAPLING: + feature = TreeFeatures.MEGA_JUNGLE_TREE.value(); + featureSmall = RBFeatures.SHORT_MEGA_JUNGLE.value(); + break; + case SPRUCE_SAPLING: + feature = TreeFeatures.MEGA_SPRUCE.value(); + featureSmall = RBFeatures.SHORT_MEGA_SPRUCE.value(); + break; + case DARK_OAK_SAPLING: + feature = TreeFeatures.DARK_OAK.value(); + featureSmall = RBFeatures.SHORT_DARK_OAK.value(); + break; + default: + return false; + } + for (int i = 0; i >= -1; --i) { + for (int j = 0; j >= -1; --j) { + if (AbstractMegaTreeGrower.isTwoByTwoSapling(state, world, pos, i, j)) { + isMega = true; //set ismega so that function above can know when a tree is 2x2 and has failed growth (admitedly this is probs not the best solution) + BlockState air = Blocks.AIR.defaultBlockState(); + world.setBlock(pos.offset(i, 0, j), air, 4); //remove saplings + world.setBlock(pos.offset(i + 1, 0, j), air, 4); + world.setBlock(pos.offset(i, 0, j + 1), air, 4); + world.setBlock(pos.offset(i + 1, 0, j + 1), air, 4); + if (feature.place(world, chunkGenerator, random, pos.offset(i, 0, j))) { + return true; + } else if(featureSmall.place(world, chunkGenerator, random, pos.offset(i, 0, j))){ //try placing short tree if normal tree fails + return true; + } else { + world.setBlock(pos.offset(i, 0, j), state, 4); //replace saplings if both fail + world.setBlock(pos.offset(i + 1, 0, j), state, 4); + world.setBlock(pos.offset(i, 0, j + 1), state, 4); + world.setBlock(pos.offset(i + 1, 0, j + 1), state, 4); + return false ; + } + } + } + } + return false; + } + + private boolean hasFlowers(LevelAccessor world, BlockPos pos) { //straight up pulled from nms code + Iterator iterator = BlockPos.MutableBlockPos.betweenClosed(pos.below().north(2).west(2), pos.above().south(2).east(2)).iterator(); + + BlockPos blockposition1; + + do { + if (!iterator.hasNext()) { + return false; + } + + blockposition1 = (BlockPos) iterator.next(); + } while (!world.getBlockState(blockposition1).is(BlockTags.FLOWERS)); + + return true; + } + + + + + @Override + public boolean deleteOnFullGrowth() { + return true; + } + + +} diff --git a/paper/src/main/java/com/untamedears/realisticbiomes/growth/RBFeatures.java b/paper/src/main/java/com/untamedears/realisticbiomes/growth/RBFeatures.java new file mode 100644 index 00000000..ce034b4e --- /dev/null +++ b/paper/src/main/java/com/untamedears/realisticbiomes/growth/RBFeatures.java @@ -0,0 +1,131 @@ +package com.untamedears.realisticbiomes.growth; + +import java.util.List; +import java.util.OptionalInt; + +import com.google.common.collect.ImmutableList; + +import net.minecraft.core.Holder; +import net.minecraft.data.worldgen.features.FeatureUtils; +import net.minecraft.util.random.SimpleWeightedRandomList; +import net.minecraft.util.valueproviders.ConstantInt; +import net.minecraft.util.valueproviders.UniformInt; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration; +import net.minecraft.world.level.levelgen.feature.featuresize.ThreeLayersFeatureSize; +import net.minecraft.world.level.levelgen.feature.featuresize.TwoLayersFeatureSize; +import net.minecraft.world.level.levelgen.feature.foliageplacers.AcaciaFoliagePlacer; +import net.minecraft.world.level.levelgen.feature.foliageplacers.BlobFoliagePlacer; +import net.minecraft.world.level.levelgen.feature.foliageplacers.DarkOakFoliagePlacer; +import net.minecraft.world.level.levelgen.feature.foliageplacers.MegaJungleFoliagePlacer; +import net.minecraft.world.level.levelgen.feature.foliageplacers.MegaPineFoliagePlacer; +import net.minecraft.world.level.levelgen.feature.foliageplacers.RandomSpreadFoliagePlacer; +import net.minecraft.world.level.levelgen.feature.foliageplacers.SpruceFoliagePlacer; +import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider; +import net.minecraft.world.level.levelgen.feature.stateproviders.WeightedStateProvider; +import net.minecraft.world.level.levelgen.feature.treedecorators.AlterGroundDecorator; +import net.minecraft.world.level.levelgen.feature.treedecorators.BeehiveDecorator; +import net.minecraft.world.level.levelgen.feature.treedecorators.LeaveVineDecorator; +import net.minecraft.world.level.levelgen.feature.treedecorators.TrunkVineDecorator; +import net.minecraft.world.level.levelgen.feature.trunkplacers.BendingTrunkPlacer; +import net.minecraft.world.level.levelgen.feature.trunkplacers.DarkOakTrunkPlacer; +import net.minecraft.world.level.levelgen.feature.trunkplacers.ForkingTrunkPlacer; +import net.minecraft.world.level.levelgen.feature.trunkplacers.GiantTrunkPlacer; +import net.minecraft.world.level.levelgen.feature.trunkplacers.MegaJungleTrunkPlacer; +import net.minecraft.world.level.levelgen.feature.trunkplacers.StraightTrunkPlacer; + +//manage NMS Configured features for RB +public class RBFeatures { + + + private static TreeConfiguration.TreeConfigurationBuilder createStraightBlobTree( + net.minecraft.world.level.block.Block log, net.minecraft.world.level.block.Block leaves, + int baseHeight, int radius) { + return new TreeConfiguration.TreeConfigurationBuilder(BlockStateProvider.simple(log), new StraightTrunkPlacer(baseHeight, 0, 0), BlockStateProvider.simple(leaves), new BlobFoliagePlacer(ConstantInt.of(radius), ConstantInt.of(0), 3), new TwoLayersFeatureSize(1, 0, 1)); + } + //tree features: + + //beehive with 5% chance of generating + private static final BeehiveDecorator BEEHIVE_005 = new BeehiveDecorator(0.05F); + //Short oak tree feature, short meaning the tree will grow at the smallest size vanilla does + public static final Holder> SHORT_OAK = + FeatureUtils.register("short_oak", Feature.TREE, createStraightBlobTree + (Blocks.OAK_LOG, Blocks.OAK_LEAVES, 4, 2).build()); + //Short birch tree feature + public static final Holder> SHORT_BIRCH = + FeatureUtils.register("short_birch", Feature.TREE, createStraightBlobTree + (Blocks.BIRCH_LOG, Blocks.BIRCH_LEAVES, 5, 2).build()); + //Short oak tree with bees feature, the chance for a beehive is vanilla's 5%, but might be worth adding an adjust to config.yaml maybe ? + public static final Holder> SHORT_OAK_BEES = + FeatureUtils.register("short_oak_bees", Feature.TREE, createStraightBlobTree + (Blocks.OAK_LOG, Blocks.OAK_LEAVES, 4, 2).decorators(List.of(BEEHIVE_005)).build()); + //Short birch tree with bees feature + public static final Holder> SHORT_BIRCH_BEES = + FeatureUtils.register("short_birch_bees", Feature.TREE, createStraightBlobTree + (Blocks.BIRCH_LOG, Blocks.BIRCH_LEAVES, 5, 2).decorators(List.of(BEEHIVE_005)).build()); + //short jungle tree feature + public static final Holder> SHORT_JUNGLE = + FeatureUtils.register("short_jungle", Feature.TREE, createStraightBlobTree + (Blocks.JUNGLE_LOG, Blocks.JUNGLE_LEAVES, 4, 2).build()); + //short spruce, this one is a fair bit longer lmao + //might also be worth adding options to configure type of grower for each tree instead of hard coding ? + //idk, I am also lazy and don't wanna wrap my head around how the config parser woks + public static final Holder> SHORT_SPRUCE = + FeatureUtils.register( + "short_spruce", Feature.TREE, ( + new TreeConfiguration.TreeConfigurationBuilder(BlockStateProvider.simple(Blocks.SPRUCE_LOG), + new StraightTrunkPlacer(5, 0, 0), BlockStateProvider.simple(Blocks.SPRUCE_LEAVES), + new SpruceFoliagePlacer(UniformInt.of(2, 3), UniformInt.of(0, 2), UniformInt.of(1, 2)), + new TwoLayersFeatureSize(2, 0, 2))).ignoreVines().build()); + //short acacia + public static final Holder> SHORT_ACACIA = + FeatureUtils.register( + "short_acacia", Feature.TREE, ( + new TreeConfiguration.TreeConfigurationBuilder(BlockStateProvider.simple(Blocks.ACACIA_LOG), + new ForkingTrunkPlacer(5, 0 ,0), BlockStateProvider.simple(Blocks.ACACIA_LEAVES), + new AcaciaFoliagePlacer(ConstantInt.of(2), ConstantInt.of(0)), + new TwoLayersFeatureSize(1, 0, 2))).ignoreVines().build()); + //short 2x2 jungle tree + public static final Holder> SHORT_MEGA_JUNGLE = + FeatureUtils.register( + "short_mega_jungle_tree", Feature.TREE, ( + new TreeConfiguration.TreeConfigurationBuilder(BlockStateProvider.simple(Blocks.JUNGLE_LOG), + new MegaJungleTrunkPlacer(10, 0, 0), BlockStateProvider.simple(Blocks.JUNGLE_LEAVES), + new MegaJungleFoliagePlacer(ConstantInt.of(2), ConstantInt.of(0), 2), + new TwoLayersFeatureSize(1, 1, 2))). + decorators(ImmutableList.of(TrunkVineDecorator.INSTANCE, LeaveVineDecorator.INSTANCE)).build()); + //short 2x2 spruce tree + //should 2x2 pine tree also be included ? + public static final Holder> SHORT_MEGA_SPRUCE = + FeatureUtils.register( + "short_mega_spruce", Feature.TREE, ( + new TreeConfiguration.TreeConfigurationBuilder(BlockStateProvider.simple(Blocks.SPRUCE_LOG), + new GiantTrunkPlacer(13, 0, 0), BlockStateProvider.simple(Blocks.SPRUCE_LEAVES), + new MegaPineFoliagePlacer(ConstantInt.of(0), ConstantInt.of(0), UniformInt.of(13, 17)), //oh maybe this uniformint is why sometimes it still fails to grow with height restriction even with the other things + new TwoLayersFeatureSize(1, 1, 2))) + .decorators(ImmutableList.of(new AlterGroundDecorator(BlockStateProvider.simple(Blocks.PODZOL)))).build()); + //short 2x2 ofc because that's the only way it grows (normally) dark oak tree + public static final Holder> SHORT_DARK_OAK = + FeatureUtils.register( + "short_dark_oak", Feature.TREE, ( + new TreeConfiguration.TreeConfigurationBuilder(BlockStateProvider.simple(Blocks.DARK_OAK_LOG), + new DarkOakTrunkPlacer(6, 0, 0), BlockStateProvider.simple(Blocks.DARK_OAK_LEAVES), + new DarkOakFoliagePlacer(ConstantInt.of(0), ConstantInt.of(0)), + new ThreeLayersFeatureSize(1, 1, 0, 1, 2, OptionalInt.empty()))).ignoreVines().build()); + //azalea + public static final Holder> SHORT_AZALEA_TREE = + FeatureUtils.register( + "short_azalea_tree", Feature.TREE, ( + new TreeConfiguration.TreeConfigurationBuilder(BlockStateProvider.simple(Blocks.OAK_LOG), + new BendingTrunkPlacer(4, 0, 0, 3, UniformInt.of(1, 2)), + new WeightedStateProvider(SimpleWeightedRandomList.builder() + .add(Blocks.AZALEA_LEAVES.defaultBlockState(), 3).add(Blocks.FLOWERING_AZALEA_LEAVES.defaultBlockState(), 1)), + new RandomSpreadFoliagePlacer(ConstantInt.of(3), ConstantInt.of(0), ConstantInt.of(2), 50), + new TwoLayersFeatureSize(1, 0, 1))).dirt(BlockStateProvider.simple(Blocks.ROOTED_DIRT)).forceDirt().build()); + + +} +