All versions
1.5.1
Release
Advanced TreeCapitator 1.5.15 months ago 50
Compatibility
Minecraft: Java Edition
1.20.x
Platforms
Bukkit
Paper
Purpur
Spigot
Changes
[1.5.1] — 2025-06-03
Fixed — Bug Fixes
| # | Bug | Details |
|---|---|---|
| 1 | BFS adds out-of-range blocks to the break-set before checking distance caps | In TreeFinder.findLogs(), logsToBreak.add(current) ran unconditionally at the start of the loop body — before the dist > MAX_DISTANCE and hdist > maxHorizontalDistance checks. A block at the outer edge of the BFS scan (e.g. hdist == maxHorizontalDistance + 1) was enqueued as a neighbour of an in-range block, then added to the break-set when dequeued, even though the check immediately after prevented its own neighbours from being explored. Result: logs slightly beyond max-horizontal-distance were still felled, making the config option unreliable at the boundary. Fixed by moving both distance checks before the logsToBreak.add() call. |
| 2 | cooldownMs loaded with (int) cast before clamping — silent integer overflow |
cooldownMs = clamp((int) cfg.getLong("cooldown-ms", 200L), 0, 60_000) cast the raw YAML long to int before clamping. Any value larger than Integer.MAX_VALUE (2 147 483 647) in the config file overflowed to a negative number, which clamp() then floored to 0, silently disabling the per-player cooldown entirely. Fixed by clamping on the long value first: (int) Math.min(Math.max(...), 60_000L). Field type also changed from long to int for consistency; getCooldownMs() now returns int. |
| 3 | Hardcoded MAX_DISTANCE = 30 truncated tall Jungle trees |
The 3-D Manhattan BFS safety cap was hardcoded as private static final int MAX_DISTANCE = 30 in TreeFinder. Jungle trees can reach 30+ blocks tall; a player chopping from a non-base position (reducing the vertical budget) or a tree with any horizontal spread would have its top logs excluded from the fell silently. Exposed as max-distance in config.yml with a default of 50. Configurable range: 10 – 200. |
| 4 | getBreakingTrees() exposed the internal mutable anti-recursion set |
The public getter getBreakingTrees() returned the raw ConcurrentHashMap.newKeySet() directly. External code (another plugin or a misbehaving task) could call .add(uuid) to permanently lock a player out of tree felling, or .clear() to disable the anti-recursion guard entirely mid-fell. Fixed by removing the public getter and replacing it with the package-private helpers markFelling(UUID) and unmarkFelling(UUID), used only by TreeCapitatorTask. |
| 5 | isOverworldLog() did not cover stripped-log variants — require-leaves bypass |
The method used a hard-coded switch that only listed nine non-stripped log variants (e.g. OAK_LOG). If a server operator added a stripped variant (e.g. STRIPPED_OAK_LOG) to log-types in config.yml, isOverworldLog() returned false (default case), and the require-leaves check was silently skipped. A player-built wall of stripped logs could be felled with a single swing even with require-leaves: true. Fixed by replacing the switch with a Material name-suffix check: any material ending in _LOG or _WOOD returns true. Nether stems (CRIMSON_STEM, WARPED_STEM), hyphae, and BAMBOO_BLOCK end in _STEM, _HYPHAE, or _BLOCK and continue to return false correctly. |
| 6 | Leaf durability hits silently discarded by integer truncation | leafBreaks / safeRatio used integer division, dropping the remainder entirely. With the default leaf-durability-ratio: 10, breaking fewer than 10 leaves contributed exactly 0 durability hits — so a small tree with leaves produced no leaf-based durability cost at all. Fixed by resolving the remainder probabilistically: remainder / safeRatio gives the probability of one additional hit, resolved with a ThreadLocalRandom roll, consistent with the existing fractional-hit logic in calcDamage(). |
| 7 | TreeCapitatorEvent API lacked @NotNull annotations |
All three public getters (getPlayer(), getLogs(), getLeaves()) and the constructor parameters had no nullability contract. Third-party plugins integrating the API had no IDE or static-analysis guidance about null safety. Added @NotNull (from org.jetbrains:annotations:24.1.0) to all public constructor parameters and return types. org.jetbrains:annotations added to pom.xml as a provided dependency. |
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:jvDx9qIW"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:8li9wnIm:jvDx9qIW"
}

