When I approach one for the first time, or plant a tree by sapling, the leaves always decay even when I don't cut the tree down.
In other words, when the tree first generates its leaves are already decaying.
Ironwood Tree Feature:
package com.kenneths_mod.generation.trees.ironwood;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import com.kenneths_mod.lists.BlockList;
import com.mojang.datafixers.Dynamic;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MutableBoundingBox;
import net.minecraft.world.gen.IWorldGenerationReader;
import net.minecraft.world.gen.feature.AbstractTreeFeature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraftforge.common.IPlantable;
public class IronwoodTreeFeature extends AbstractTreeFeature<NoFeatureConfig> {
private static final BlockState LOG = BlockList.ironwood_log.getDefaultState();
private static final BlockState LEAF = BlockList.ironwood_leaves.getDefaultState();
private static final IPlantable SAPLING = (IPlantable)BlockList.ironwood_sapling;
protected final int minHeight = 4;
public IronwoodTreeFeature (Function<Dynamic<?>, ? extends NoFeatureConfig> configIn, boolean doBlockNotifyIn) {
super(configIn, doBlockNotifyIn);
this.setSapling(SAPLING);
}
@Override
protected boolean place(Set<BlockPos> changedBlocks, IWorldGenerationReader worldIn, Random rand, BlockPos position, MutableBoundingBox boundingBox) {
int height = this.getHeight(rand);
boolean flag = true;
if (position.getY() >= 1 && position.getY() + height + 1 <= worldIn.getMaxHeight()) {
for(int j = position.getY(); j <= position.getY() + 1 + height; ++j) {
int k = 1;
if (j == position.getY()) {
k = 0;
}
if (j >= position.getY() + 1 + height - 2) {
k = 2;
}
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
for(int l = position.getX() - k; l <= position.getX() + k && flag; ++l) {
for(int i1 = position.getZ() - k; i1 <= position.getZ() + k && flag; ++i1) {
if (j >= 0 && j < worldIn.getMaxHeight()) {
if (!func_214587_a(worldIn, blockpos$mutableblockpos.setPos(l, j, i1))) {
flag = false;
}
} else {
flag = false;
}
}
}
}
if(!flag) {
return false;
} else if (isSoil(worldIn, position.down(), getSapling()) && position.getY() < worldIn.getMaxHeight() - height - 1) {
this.setDirtAt(worldIn, position.down(), position);
for(int l2 = position.getY() - 3 + height; l2 <= position.getY() + height; ++l2) {
int l3 = l2 - (position.getY() + height);
int j4 = 1 - l3 / 2;
for(int j1 = position.getX() - j4; j1 <= position.getX() + j4; ++j1) {
int k1 = j1 - position.getX();
for(int l1 = position.getZ() - j4; l1 <= position.getZ() + j4; ++l1) {
int i2 = l1 - position.getZ();
if (Math.abs(k1) != j4 || Math.abs(i2) != j4 || rand.nextInt(2) != 0 && l3 != 0) {
BlockPos blockpos = new BlockPos(j1, l2, l1);
if (isAirOrLeaves(worldIn, blockpos) || func_214576_j(worldIn, blockpos)) {
this.setLogState(changedBlocks, worldIn, blockpos, LEAF, boundingBox);
}
}
}
}
}
for(int i3 = 0; i3 < height; ++i3) {
//if (isAirOrLeaves(worldIn, position.up(i3)) || func_214576_j(worldIn, position.up(i3))) {
//this.setLogState(changedBlocks, worldIn, position.up(i3), LOG, boundingBox);
//}
this.setLogState(changedBlocks, worldIn, position.up(i3), LOG, boundingBox);
}
return true;
} else {
return false;
}
} else {
return false;
}
}
@Override
protected IPlantable getSapling() {
return SAPLING;
}
protected int getHeight (Random rand) {
return this.minHeight + rand.nextInt(3);
}
}
Ironwood Leaves:
package com.kenneths_mod.generation.trees.ironwood;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.LeavesBlock;
import net.minecraft.entity.EntityType;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class IronwoodLeaves extends LeavesBlock {
public static final IntegerProperty DISTANCE = BlockStateProperties.DISTANCE_1_7;
public static final BooleanProperty PERSISTENT = BlockStateProperties.PERSISTENT;
protected static boolean renderTranslucent;
public IronwoodLeaves(Properties properties) {
super(properties);
this.setDefaultState(this.stateContainer.getBaseState().with(DISTANCE, Integer.valueOf(7)).with(PERSISTENT, Boolean.valueOf(false)));
}
@Override
public boolean ticksRandomly(BlockState state) {
return state.get(DISTANCE) == 7 && !state.get(PERSISTENT);
}
public void randomTick(BlockState state, World worldIn, BlockPos pos, Random random) {
if (!state.get(PERSISTENT) && state.get(DISTANCE) == 7) {
spawnDrops(state, worldIn, pos);
worldIn.removeBlock(pos, false);
}
}
@Override
public void tick(BlockState state, World worldIn, BlockPos pos, Random random) {
worldIn.setBlockState(pos, updateDistance(state, worldIn, pos), 3);
}
@Override
public int getOpacity(BlockState state, IBlockReader worldIn, BlockPos pos) {
return 1;
}
@Override
public BlockState updatePostPlacement(BlockState stateIn, Direction facing, BlockState facingState, IWorld worldIn, BlockPos currentPos, BlockPos facingPos) {
int i = getDistance(facingState) + 1;
if (i != 1 || stateIn.get(DISTANCE) != i) {
worldIn.getPendingBlockTicks().scheduleTick(currentPos, this, 1);
}
return stateIn;
}
private static BlockState updateDistance (BlockState p_208493_0_, IWorld p_208493_1_, BlockPos p_208493_2_) {
int i = 7;
try (BlockPos.PooledMutableBlockPos blockpos$pooledmutableblockpos = BlockPos.PooledMutableBlockPos.retain()) {
for(Direction direction : Direction.values()) {
blockpos$pooledmutableblockpos.setPos(p_208493_2_).move(direction);
i = Math.min(i, getDistance(p_208493_1_.getBlockState(blockpos$pooledmutableblockpos)) + 1);
if (i == 1) {
break;
}
}
}
return p_208493_0_.with(DISTANCE, Integer.valueOf(i));
}
public static int getDistance(BlockState neighbor) {
if (BlockTags.LOGS.contains(neighbor.getBlock())) {
return 0;
} else {
return neighbor.getBlock() instanceof LeavesBlock ? neighbor.get(DISTANCE) : 7;
}
}
@Override
public void animateTick(BlockState stateIn, World worldIn, BlockPos pos, Random rand) {
super.animateTick(stateIn, worldIn, pos, rand);
if (worldIn.isRainingAt(pos.up())) {
if (rand.nextInt(15) == 1) {
BlockPos blockpos = pos.down();
BlockState blockstate = worldIn.getBlockState(blockpos);
if (!blockstate.isSolid() || !blockstate.func_224755_d(worldIn, blockpos, Direction.UP)) {
double d0 = (double)((float)pos.getX() + rand.nextFloat());
double d1 = (double)pos.getY() - 0.05D;
double d2 = (double)((float)pos.getZ() + rand.nextFloat());
worldIn.addParticle(ParticleTypes.DRIPPING_WATER, d0, d1, d2, 0.0D, 0.0D, 0.0D);
}
}
}
}
@OnlyIn(Dist.CLIENT)
public static void setRenderTranslucent(boolean renderTranslucent) {
IronwoodLeaves.renderTranslucent = renderTranslucent;
}
@Override
public boolean isSolid(BlockState state) {
return false;
}
@Override
public BlockRenderLayer getRenderLayer() {
return renderTranslucent ? BlockRenderLayer.CUTOUT_MIPPED : BlockRenderLayer.SOLID;
}
@Override
public boolean causesSuffocation(BlockState state, IBlockReader worldIn, BlockPos pos) {
return false;
}
@Override
public boolean canEntitySpawn(BlockState state, IBlockReader worldIn, BlockPos pos, EntityType<?> type) {
return type == EntityType.OCELOT || type == EntityType.PARROT;
}
@Override
protected void fillStateContainer(Builder<Block, BlockState> builder) {
super.fillStateContainer(builder);
builder.add(DISTANCE, PERSISTENT);
}
@Override
public BlockState getStateForPlacement(BlockItemUseContext context) {
return updateDistance(this.getDefaultState().with(PERSISTENT, Boolean.valueOf(true)), context.getWorld(), context.getPos());
}
}
Ironwood Tree:
package com.kenneths_mod.generation.trees.ironwood;
import java.util.Random;
import javax.annotation.Nullable;
import net.minecraft.block.trees.Tree;
import net.minecraft.world.gen.feature.AbstractTreeFeature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
public class IronwoodTree extends Tree {
@Nullable
protected AbstractTreeFeature<NoFeatureConfig> getTreeFeature(Random random) {
return new IronwoodTreeFeature(NoFeatureConfig::deserialize, true);
}
}
And my main class which I use to add the tree to biomes:
package com.kenneths_mod;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.kenneths_mod.generation.trees.KenmodTreeFeaturesList;
import com.kenneths_mod.generation.trees.ironwood.IronwoodSapling;
import com.kenneths_mod.generation.trees.ironwood.IronwoodTree;
import com.kenneths_mod.lists.BlockList;
import com.kenneths_mod.lists.ItemList;
import net.minecraft.block.Block;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.LogBlock;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.feature.IFeatureConfig;
import net.minecraft.world.gen.placement.AtSurfaceWithExtraConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
@Mod("kenmod")
public class KennethsModMain {
public static KennethsModMain instance;
public static final String modid = "kenmod";
private static final Logger logger = LogManager.getLogger(modid);
public KennethsModMain() {
instance = this;
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::clientRegistries);
MinecraftForge.EVENT_BUS.register(this);
}
//Use to be pre-init:
private void setup (final FMLCommonSetupEvent event) {
addFeatures();
logger.info("Settup method registered...");
}
//For things like models:
private void clientRegistries(final FMLClientSetupEvent event) {
logger.info("Client registries method registered...");
}
@Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
public static class RegistryEvents {
@SubscribeEvent
public static void registerItems (final RegistryEvent.Register<Item> event) {
event.getRegistry().registerAll(
ItemList.steel_ingot = new Item(new Item.Properties().group(ItemGroup.MISC)).setRegistryName(location("steel_ingot")),
ItemList.steel_block = new BlockItem(BlockList.steel_block, new Item.Properties().group(ItemGroup.BUILDING_BLOCKS)).setRegistryName(BlockList.steel_block.getRegistryName()),
ItemList.ironwood_log = new BlockItem(BlockList.ironwood_log, new Item.Properties().group(ItemGroup.BUILDING_BLOCKS)).setRegistryName(BlockList.ironwood_log.getRegistryName()),
ItemList.ironwood_leaves = new BlockItem(BlockList.ironwood_leaves, new Item.Properties().group(ItemGroup.DECORATIONS)).setRegistryName(BlockList.ironwood_leaves.getRegistryName()),
ItemList.ironwood_sapling = new BlockItem(BlockList.ironwood_sapling, new Item.Properties().group(ItemGroup.DECORATIONS)).setRegistryName(BlockList.ironwood_sapling.getRegistryName())
);
logger.info("Items registered...");
}
@SubscribeEvent
public static void registerBlocks (final RegistryEvent.Register<Block> event) {
event.getRegistry().registerAll(
BlockList.steel_block = new Block(Block.Properties.create(Material.IRON).hardnessAndResistance(6.0f, 40.0f).sound(SoundType.METAL)).setRegistryName(location("steel_block")),
BlockList.ironwood_log = new LogBlock(MaterialColor.OBSIDIAN, Block.Properties.create(Material.WOOD, MaterialColor.OBSIDIAN).hardnessAndResistance(4.0F).sound(SoundType.WOOD)).setRegistryName(location("ironwood_log")),
BlockList.ironwood_leaves = new LeavesBlock(Block.Properties.create(Material.LEAVES).hardnessAndResistance(0.2f).tickRandomly().sound(SoundType.PLANT)).setRegistryName(location("ironwood_leaves")),
BlockList.ironwood_sapling = new IronwoodSapling(new IronwoodTree(), Block.Properties.create(Material.PLANTS).doesNotBlockMovement().tickRandomly().hardnessAndResistance(0.5f, 0.5f).sound(SoundType.PLANT)).setRegistryName(location("ironwood_sapling"))
);
logger.info("Blocks registered...");
}
}
private static ResourceLocation location (String name) {
return new ResourceLocation(modid, name);
}
private void addFeatures () {
//Add the tree to a default biome:
Biomes.FOREST.addFeature(GenerationStage.Decoration.VEGETAL_DECORATION, Biome.createDecoratedFeature(KenmodTreeFeaturesList.ironwood_feature, IFeatureConfig.NO_FEATURE_CONFIG, Placement.COUNT_EXTRA_HEIGHTMAP, new AtSurfaceWithExtraConfig(0, 0.2F, 1)));
Biomes.PLAINS.addFeature(GenerationStage.Decoration.VEGETAL_DECORATION, Biome.createDecoratedFeature(KenmodTreeFeaturesList.ironwood_feature, IFeatureConfig.NO_FEATURE_CONFIG, Placement.COUNT_EXTRA_HEIGHTMAP, new AtSurfaceWithExtraConfig(0, 0.2F, 1)));
}
public void Debug (String toSay) {
logger.info(toSay);
}
}