-
-
Notifications
You must be signed in to change notification settings - Fork 23
Adding compatibility with TreeChop
For TreeChop to detect modded trees, only two things are needed:
- Log blocks should have the
#minecraft:logsor#minecraft:logs_that_burntag - Leaves blocks should have the
#mincraft:leavestag
And that's it!
If for some reason your "tree" blocks shouldn't have the normal logs and leaves tags (things like huge mushrooms etc.), there are two alternative, more specific tags: #treechop:choppables (for blocks that can be chopped) and #treechop:leaves_like (for connected blocks that should automatically break when the "tree" is felled).
- "TreeChop isn't working" - By default, TreeChop only detects a tree if it is connected to leaves blocks (anything with the
#minecraft:leavesor#treechop:leaves_liketags) - "Felling a tree leaves behind floating logs" - TreeChop doesn't detect logs that aren't touching each other. This often happens when log blocks are separated by leaves
- "Felling a tree leaves behind floating leaves" - TreeChop doesn't detect leaves that aren't touching each other
Special chopping loot tables can be used to drop loot or run functions whenever a block is chopped. The chopping loot table has all the functionality of the vanilla blocks table, but with two added conditions (with more to come in the future, feel free to make suggestions):
-
treechop:count_block_chops: Check the number of chops on the block. Specified as arangein the same way as theminecraft:value_checkpredicate: it can either be an integer, or a range object withminandmax(inclusive) properties. Both cases are shown in the example below. -
treechop:tree_felled: True if this chop also felled the tree. This only happens for the block that was chopped.
Things to note:
- The
choppingloot table is check for each single chop that is performed. This matters for modded tools that perform more than one chop. For example, if a tool performs 3 chops when breaking a block, the loot table will be checked 3 times, each time incrementing the chop count. - If a chopped block is broken normally (e.g., while sneaking) or as part of a felled tree, the regular
blocksloot table drops will spawn, if any. This includes whenever thetreechop:tree_felledcondition is met.
In the example below, chopping will drop a minecraft:coal on the first two chops and a minecraft:diamond when the tree is felled:
data/treechop/loot_tables/chopping/chopped_log.json
{
"type": "treechop:chopping",
"pools": [
{
"rolls": 1,
"bonus_rolls": 0,
"entries": [
{
"type": "minecraft:alternatives",
"children": [
{
"type": "minecraft:item",
"name": "minecraft:coal",
"conditions": [
{
"condition": "treechop:count_block_chops",
"range": {
"min": 1,
"max": 2
}
}
]
},
{
"type": "minecraft:item",
"name": "minecraft:diamond",
"conditions": [
{
"condition": "treechop:tree_felled"
}
]
}
]
}
]
}
]
}The API enables more advanced control over chopping mechanics. All API-related classes are found in the ht.treechop.api package, and the source files are split across three locations:
- Shared (Fabric & Forge): https://github.com/hammertater/treechop/tree/main/shared/src/main/java/ht/treechop/api.
- Fabric only: https://github.com/hammertater/treechop/tree/main/fabric/src/main/java/ht/treechop/api
- Forge only: https://github.com/hammertater/treechop/tree/main/forge/src/main/java/ht/treechop/api
Most classes are defined in Shared. The Fabric- and Forge-specific bits of the API are primarily for adding event listeners.
The safest way to use the API is to first get a TreeChopAPI instance, which provides the following functionalities:
- Enable/disable chopping for specific blocks*
- Mark blocks as leaves (or not)*
- Enable/disable chopping for specific items*
- Register handlers for blocks and items to change chopping-related behaviors
Changes to blocks or items via the API will override the settings specified in config/treechop-common.toml. For example, if a user tries to blacklist a block from being choppable, it will still be choppable if it was marked as such using the API. So, use with caution, and only for things that can't be accomplished using block tags (see previous section).
The methods provided by TreeChopAPI are documented in the source code.
With Forge, an API instance can be retrieved using the InterModEnqueueEvent:
static TreeChopAPI api = null;
@SubscribeEvent
public static void enqueueIMC(InterModEnqueueEvent event) {
InterModComms.sendTo("treechop", "getTreeChopAPI", () -> (Consumer<TreeChopAPI>) response -> {
api = response;
});
}With Fabric, an API instance can be retrieved using the "treechop:api_provider" in the shared object store:
TreeChopAPI api = null;
FabricLoader.getInstance().getObjectShare().whenAvailable("treechop:api_provider", (key, value) -> {
if (value instanceof ITreeChopAPIProvider provider) {
api = provider.get("your_mod_id");
}
});As mentioned earlier, simply marking a block as "choppable" should be done using block tags. The advantage of using the API is that changes can be made at runtime. Still, avoid doing this unless absolutely necessary. Note that TreeChop does not sync these changes between clients and servers.
public static void makePlanksChoppable(TreeChopAPI api) {
api.overrideChoppableBlock(Blocks.OAK_PLANKS, true);
}Normally, when a block is chopped, TreeChop tries to look up a stripped version of the block, then uses textures from the model assigned to the stripped blockstate. So, to change the chopped texture for a block, we can point TreeChop to a different stripped blockstate using the IStrippableBlock interface. Let's create a handler that changes the chopped texture for the minecraft:mushroom_stem block to look like minecraft:gold_block:
public class MushroomStemHandler implements ht.treechop.api.IStrippableBlock{
@Override
public BlockState getStrippedState(BlockGetter level, BlockPos pos, BlockState blockState) {
return Blocks.GOLD_BLOCK.defaultBlockState();
}
}and use a TreeChopAPI to register the handler:
public static void registerMushroomStemHandler(TreeChopAPI api) {
api.registerChoppableBlockBehavior(Blocks.MUSHROOM_STEM, new MushroomStemHandler());
}And now mushrooms look like gold blocks when chopped:

Some mods add logs that are slimmer than full-size blocks. If slim block are treated like normal logs, the chopped block will start bigger than the original block. To fix this, use the ISimpleChoppableBlock interface, which provides default implementations for many of the block-related interfaces in the API. Override the getRadius method to return the radius of the slim log (for reference, full blocks have radius 8, and each chop reduces the radius by 1). Note that this has some implications on how many chops a tree will take to fell, as documented in the source code.
public class SlimLogHandler implements ISimpleChoppableBlock {
@Override
public int getRadius(BlockGetter level, BlockPos blockPos, BlockState blockState) {
return 5;
}
}As before, use a TreeChopAPI to register the handler:
public static void registerMushroomStemHandler(TreeChopAPI api) {
api.registerChoppableBlockBehavior(MyBlocks.SLIM_LOG, new SlimLogHandler());
}The IChoppingItem can be used to change when an item can be used to chop (maybe it only chops when powered), and how many chops it performs when breaking a block (the default is 1). Let's make minecraft:diamond_axe perform 2 chops instead of 1:
public class SuperAxeHandler implements IChoppingItem {
@Override
public boolean canChop(Player player, ItemStack tool, Level level, BlockPos pos, BlockState target) {
return true; // Always allow chopping
}
@Override
public int getNumChops(ItemStack tool, BlockState target) {
return 2; // When breaking a block, add 2 chops instead of 1
}A TreeChopAPI can register the handler:
public static void registerSuperAxe(TreeChopAPI api) {
api.registerChoppingItemBehavior(Items.DIAMOND_AXE, new SuperAxeHandler());
}Instead of using handlers, classes that extend Block or Item can implement TreeChop interfaces directly. This can be useful when you want all blocks (or items) of a certain class to have special behaviors, but don't know the list of block (or item) IDs that use that class (for example, when a block class is defined in a library for use by other mods). To avoid creating a required dependency on TreeChop, interfaces can be implemented using mixins. For example:
@Mixin(SomeLogBlock.class)
public class SomeLogBlockMixin implements ISimpleChoppableBlock {
@Override
public int getRadius(BlockGetter level, BlockPos blockPos, BlockState blockState) {
return 5;
}
}