All versions
1.5.0
Release
Advanced TreeCapitator 1.5.05 months ago 81
Compatibility
Minecraft: Java Edition
1.20.x
Platforms
Bukkit
Paper
Purpur
Spigot
Changes
[1.5.0] — 2025-05-21
Fixed — Bug Fixes
| # | Bug | Details |
|---|---|---|
| 1 | Enchantment.getByKey() silently returned null on Paper 1.21+ |
DurabilityHandler.getUnbreakingLevel() used the deprecated Enchantment.getByKey(NamespacedKey.minecraft("unbreaking")) lookup. On Paper 1.21+ this returns null if the registry isn't fully initialised, causing the method to always return 0 — i.e. Unbreaking was completely ignored and axes lost durability as if they had no enchantment. Fixed by using the stable constant Enchantment.UNBREAKING. |
| 2 | Player offline during 1-tick scheduling delay caused unsafe state | TreeCapitatorTask ran 1 tick after BlockBreakEvent. If the player disconnected in that window, player.getInventory() and player.playSound() were called on an invalid player. Fixed by adding an isOnline() guard at the top of doFell(). |
| 3 | disabledPlayers set accumulated stale UUIDs indefinitely |
When a player used /atc toggle to disable felling and then quit the server, their UUID was never removed from disabledPlayers. On re-join, felling remained disabled without the player having toggled it again. Fixed by handling PlayerQuitEvent and clearing all per-player state (disabledPlayers, cooldowns, breakingTrees). |
| 4 | Cooldown was consumed even when TreeCapitatorEvent was cancelled |
The cooldown timestamp was written in onBlockBreak() before the task ran. If a third-party plugin cancelled TreeCapitatorEvent (e.g. an economy plugin denying the action), the player was still forced to wait out the full cooldown. Fixed by refunding (removing) the cooldown entry inside doFell() immediately after a cancellation is detected. |
| 5 | TreeCapitatorEvent Javadoc incorrectly stated "excludes origin" |
The @param logs Javadoc said the set excluded the origin block. In reality trimmedLogs always includes the origin. Third-party plugins that relied on the documented behaviour miscounted tree size and produced incorrect reward calculations. Corrected the Javadoc; no runtime behaviour changed. |
| 6 | Unbreaking durability calculation was deterministic (did not match vanilla) | calcDamage() multiplied hits by the expected-value probability 1/(level+1), producing the same result every time. Vanilla Minecraft rolls each hit independently: each has a 1/(level+1) chance to deal damage. With Unbreaking III chopping 20 logs, the plugin always applied exactly 5 durability; vanilla would apply anywhere from 0 to 20. Fixed by replacing the formula with a per-hit ThreadLocalRandom roll matching the vanilla mechanic. |
| 7 | Sound effect played at player location instead of the tree | player.playSound(player.getLocation(), ...) meant the wood-break sound originated from the player rather than the tree. Players nearby heard nothing; the audio source felt disconnected from the visual effect. Fixed by using world.playSound(startBlock.getLocation(), ...) so the sound radiates from the felled tree's base and all nearby players can hear it. |
Added — New Features
| Feature | Details |
|---|---|
max-horizontal-distance is now configurable |
Previously MAX_HORIZONTAL_DISTANCE = 8 was a hardcoded constant in TreeFinder. It is now exposed as max-horizontal-distance in config.yml (default: 8). Modpack trees with unusually wide canopies can increase this; densely planted farms can decrease it to prevent cross-tree BFS merging. |
Changed — Code Improvements
- Cooldown cleanup interval is now dynamic: entries are removed after
max(5 s, 10 × cooldown-ms)instead of a flat 60 seconds. With the default 200 ms cooldown this reduces the time stale entries linger from 60 s to 2 s. Config.getMaxHorizontalDistance()added;TreeFinder.findLogs()now accepts this as a parameter instead of reading a static field.AdvancedTreeCapitator.getCooldowns()added to allowTreeCapitatorTaskto refund cooldowns without reflection.- All per-player state is now cleared in
PlayerQuitEventin addition to each task'sfinallyblock.
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:8li9wnIm:vbXD8kKT"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:8li9wnIm:vbXD8kKT"
}

