Skip to content
This repository was archived by the owner on Feb 18, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ dependency-reduced-pom.xml
.gradle
build
libs/
paper/run
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
@@ -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();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small note but does NMS not contain a method to do this for us?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically checking nearby for flowers

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it does but it's private so I copied it over

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()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this iterator loop be better as a

while(iter.hasNext()) {
 //logic etc
}

return false;
}

blockposition1 = (BlockPos) iterator.next();
} while (!world.getBlockState(blockposition1).is(BlockTags.FLOWERS));

return true;
}




@Override
public boolean deleteOnFullGrowth() {
return true;
}


}
Original file line number Diff line number Diff line change
@@ -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<ConfiguredFeature<TreeConfiguration, ?>> 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<ConfiguredFeature<TreeConfiguration, ?>> 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<ConfiguredFeature<TreeConfiguration, ?>> 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<ConfiguredFeature<TreeConfiguration, ?>> 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<ConfiguredFeature<TreeConfiguration, ?>> 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<ConfiguredFeature<TreeConfiguration, ?>> 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<ConfiguredFeature<TreeConfiguration, ?>> 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<ConfiguredFeature<TreeConfiguration, ?>> 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<ConfiguredFeature<TreeConfiguration, ?>> 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<ConfiguredFeature<TreeConfiguration, ?>> 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<ConfiguredFeature<TreeConfiguration, ?>> 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.<BlockState>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());


}