Skip to content
Merged
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
7 changes: 6 additions & 1 deletion .github/workflows/publish-modrinth.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
types: [published]

permissions:
contents: read
contents: write # to attach the zip to the GitHub release

jobs:
publish:
Expand All @@ -20,6 +20,11 @@ jobs:
- name: Build the datapack zip
run: git archive --format=zip -o "Timber-${{ github.event.release.tag_name }}.zip" HEAD -- data 121 pack.mcmeta pack.png LICENSE README.md

- name: Attach the zip to the GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ github.event.release.tag_name }}" "Timber-${{ github.event.release.tag_name }}.zip" --repo "$GITHUB_REPOSITORY"

- name: Read the supported Minecraft version range
id: versions
run: |
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ jobs:
echo "- Minecraft ${{ matrix.minecraft }}: $line" >> "$GITHUB_STEP_SUMMARY"
done || true

# informational: performance counters from the benchmark test
- name: Report benchmark results
if: always()
run: |
grep -h 'TIMBER-BENCHMARK' server-output.log | sed 's/.*TIMBER-BENCHMARK //' | while read -r line; do
echo "::notice title=Benchmark (Minecraft ${{ matrix.minecraft }})::$line"
echo "- Minecraft ${{ matrix.minecraft }}: $line" >> "$GITHUB_STEP_SUMMARY"
done || true

# single check to require in the branch ruleset, so changing the matrix doesn't need a ruleset update
test-result:
name: Test result
Expand Down
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ Release notes live on the [GitHub Releases page](https://github.com/Moggla/Timbe
## Releasing

1. If the supported MC versions changed: bump `pack.mcmeta` (`min_format`/`max_format`) and match `.github/mc-version-range.txt` to it. [Pack format](https://minecraft.wiki/w/Pack_format) lists which MC version has which format number.
2. Update the version string in `data/timber/dialog/help.json`.
3. Tag the commit `vX.Y.Z` and publish it as a GitHub Release, using "Generate release notes" for the body; that triggers the Modrinth upload automatically.
2. Update the version string in `data/timber/dialog/help.json` and in `data/timber/function/settings/open/menu.mcfunction` (the Settings dialog body).
3. Tag the commit `vX.Y.Z` and publish it as a GitHub Release, using "Generate release notes" for the body; that triggers the Modrinth upload and attaches the datapack zip to the release automatically.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
scoreboard players add leaf_search_steps timber_bench 1
scoreboard players add leaf_distance timber 1
# search for next cap
execute positioned ~ ~ ~1 unless entity @e[type=minecraft:marker,tag=timber_cap,distance=...1,sort=arbitrary,limit=1] if predicate timber:block/cap run function timber:algorithm/fungus/leaves/destroy
Expand Down
2 changes: 2 additions & 0 deletions data/timber/function/algorithm/tree/leaves/search.mcfunction
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
scoreboard players add leaf_search_steps timber_bench 1

# save leaves distance tag at its position
summon marker ~ ~ ~ {Tags:["timber_leaf_distance"]}
scoreboard players operation @e[type=minecraft:marker,tag=timber_leaf_distance,distance=...1,sort=arbitrary,limit=1] timber = leaf_distance timber
Expand Down
2 changes: 2 additions & 0 deletions data/timber/function/algorithm/tree/search_2.mcfunction
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
scoreboard players add log_probes timber_bench 1

# if log
execute if predicate timber:block/log unless entity @e[type=minecraft:marker,tag=timber_log,distance=...1,sort=arbitrary,limit=1] run function timber:algorithm/tree/chop
# if root
Expand Down
1 change: 1 addition & 0 deletions data/timber/function/init.mcfunction
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ scoreboard objectives add timber_menu dummy
scoreboard objectives add timber_disabled dummy
scoreboard objectives remove timber_prsistent
scoreboard objectives add timber_persistent dummy
scoreboard objectives add timber_bench dummy

scoreboard players reset * TimberSettings
scoreboard players reset * TimberSetBlocks
Expand Down
5 changes: 5 additions & 0 deletions data/timber/function/run.mcfunction
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ scoreboard players set leaves_found timber 0
scoreboard players set tree_size timber 0
scoreboard players set tree_type timber 0

# performance counters (see #11): how many times the traversal probes for a neighboring log/root, and how many times it
# (re-)enters the leaf/cap search. A dedicated benchmark test resets and reads these to get a comparable number across changes.
scoreboard players set log_probes timber_bench 0
scoreboard players set leaf_search_steps timber_bench 0

# load settings to other scoreboard (leaves persistent)
execute unless score persistent timber matches 1.. run scoreboard players set @s timber_persistent 0
execute if score persistent timber matches 1.. run scoreboard players set @s timber_persistent 1
Expand Down
2 changes: 1 addition & 1 deletion data/timber/function/settings/open/menu.mcfunction
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# opens the "Settings" settings page with the current values
# only reachable for admins
data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Settings","external_title":"Timber Settings","body":{"type":"minecraft:plain_message","contents":"Changes are saved for the whole server.","width":300},"inputs":[],"can_close_with_escape":true,"pause":false,"after_action":"none","columns":2,"actions":[],"exit_action":{"label":{"translate":"gui.back"},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 98"}}}
data modify storage timber:settings dialog set value {"type":"minecraft:multi_action","title":"Timber Settings","external_title":"Timber Settings","body":{"type":"minecraft:plain_message","contents":["",{"text":"Version v8.0.0","color":"#724E21"},"\n\n",{"text":"Changes are saved for the whole server."}],"width":300},"inputs":[],"can_close_with_escape":true,"pause":false,"after_action":"none","columns":2,"actions":[],"exit_action":{"label":{"translate":"gui.back"},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 98"}}}
execute if score standing timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Works When Standing"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 100"}}
execute unless score standing timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Works When Standing"},{"translate":"options.off","color":"red"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 100"}}
execute if score sneaking timber matches 1 run data modify storage timber:settings dialog.actions append value {"label":{"translate":"options.generic_value","with":[{"text":"Works When Sneaking"},{"translate":"options.on","color":"green"}]},"width":150,"action":{"type":"minecraft:run_command","command":"trigger TimberSettings set 101"}}
Expand Down
1 change: 1 addition & 0 deletions data/timber/function/uninstall.mcfunction
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ scoreboard objectives remove TimberSetLeaves
scoreboard objectives remove timber_menu
scoreboard objectives remove timber_disabled
scoreboard objectives remove timber_persistent
scoreboard objectives remove timber_bench

# remove markers
kill @e[type=minecraft:marker,tag=timber_slow_chop]
Expand Down
7 changes: 7 additions & 0 deletions tests/data/timber_test/function/report_benchmark.mcfunction
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#> Prints the performance counters of the chop that just ran (see run.mcfunction's "performance counters" reset). Same two paths as
#> the warn_* helpers (log line for the CI, chat for players). Not a warning: nothing here ever fails the test.
#> @param test name of the test
#> @param log_probes value of "log_probes timber_bench" after the chop
#> @param leaf_search_steps value of "leaf_search_steps timber_bench" after the chop
$execute unless entity @a[tag=!timber_test_dummy] run say TIMBER-BENCHMARK $(test): log_probes=$(log_probes) leaf_search_steps=$(leaf_search_steps)
$tellraw @a[tag=!timber_test_dummy] {"text":"Benchmark $(test): log_probes=$(log_probes) leaf_search_steps=$(leaf_search_steps)","color":"aqua"}
116 changes: 116 additions & 0 deletions tests/data/timber_test/test/benchmark_tree_shapes.mcfunction
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#> Not a correctness test: reports the performance counters (see #11) for four deterministic, hand-built shapes, so the
#> numbers are exactly reproducible across runs and comparable before/after an algorithm change: a small baseline tree,
#> then three ~38-log stress shapes spread long horizontally, straight up, and branching in every direction, to see
#> whether a fix helps all shapes equally or just one. Runs alone (its own environment): the counters are shared
#> datapack-wide and would be corrupted by another chop happening at the same time.
#> Every leaf is placed directly face-adjacent to a log (a "+" cross around each log run, no diagonal corners): the leaf
#> search only steps to a strictly farther "distance" than where it came from, so a hand-built same-distance leaf that
#> isn't itself touching a log needs a real, increasing distance gradient to be reachable, which it doesn't have.
# @environment timber_test:benchmark
# @template timber_test:arena
# @timeout 400
# @skyaccess true

function timber_test:platform
await delay 1s
function timber_test:player {name:"bench_t",axe:"minecraft:netherite_axe"}

# --- small: a normal-sized hand-built oak, the baseline everything else is compared against
fill ~19 ~1 ~20 ~19 ~4 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~21 ~1 ~20 ~21 ~4 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~1 ~19 ~20 ~4 ~19 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~1 ~21 ~20 ~4 ~21 minecraft:oak_leaves[persistent=false,distance=1]
setblock ~20 ~5 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~1 ~20 ~20 ~4 ~20 minecraft:oak_log[axis=y]
assert block ~20 ~1 ~20 minecraft:oak_log
dummy bench_t mine ~20 ~1 ~20
await not block ~20 ~1 ~20 #minecraft:logs
await delay 5t
await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39]
scoreboard objectives add timber_test dummy
data modify storage timber_test:tmp test set value "benchmark_small"
execute store result storage timber_test:tmp log_probes int 1 run scoreboard players get log_probes timber_bench
execute store result storage timber_test:tmp leaf_search_steps int 1 run scoreboard players get leaf_search_steps timber_bench
function timber_test:report_benchmark with storage timber_test:tmp
function timber_test:assert_no_tree {test:"benchmark_small"}

# --- long horizontal: a 37 log line along x, mined at its middle (the initial chop detection only looks for the dropped
# item within 7 blocks of whoever mined, so this can't be mined from far away like the tall/bushy shapes mine their base)
fill ~2 ~2 ~20 ~38 ~2 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~2 ~0 ~20 ~38 ~0 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~2 ~1 ~19 ~38 ~1 ~19 minecraft:oak_leaves[persistent=false,distance=1]
fill ~2 ~1 ~21 ~38 ~1 ~21 minecraft:oak_leaves[persistent=false,distance=1]
setblock ~1 ~1 ~20 minecraft:oak_leaves[persistent=false,distance=1]
setblock ~39 ~1 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~2 ~1 ~20 ~38 ~1 ~20 minecraft:oak_log[axis=x]
assert block ~20 ~1 ~20 minecraft:oak_log
dummy bench_t mine ~20 ~1 ~20
await not block ~20 ~1 ~20 #minecraft:logs
await delay 5t
await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39]
data modify storage timber_test:tmp test set value "benchmark_long_horizontal"
execute store result storage timber_test:tmp log_probes int 1 run scoreboard players get log_probes timber_bench
execute store result storage timber_test:tmp leaf_search_steps int 1 run scoreboard players get leaf_search_steps timber_bench
function timber_test:report_benchmark with storage timber_test:tmp
function timber_test:assert_no_tree {test:"benchmark_long_horizontal"}

# --- tall: a 37 log column straight up, mined at its base (close to where the dummy spawns)
fill ~19 ~1 ~20 ~19 ~37 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~21 ~1 ~20 ~21 ~37 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~1 ~19 ~20 ~37 ~19 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~1 ~21 ~20 ~37 ~21 minecraft:oak_leaves[persistent=false,distance=1]
setblock ~20 ~38 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~1 ~20 ~20 ~37 ~20 minecraft:oak_log[axis=y]
assert block ~20 ~1 ~20 minecraft:oak_log
dummy bench_t mine ~20 ~1 ~20
await not block ~20 ~1 ~20 #minecraft:logs
await delay 5t
await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39]
data modify storage timber_test:tmp test set value "benchmark_tall"
execute store result storage timber_test:tmp log_probes int 1 run scoreboard players get log_probes timber_bench
execute store result storage timber_test:tmp leaf_search_steps int 1 run scoreboard players get leaf_search_steps timber_bench
function timber_test:report_benchmark with storage timber_test:tmp
function timber_test:assert_no_tree {test:"benchmark_tall"}

# --- bushy: a 10 tall trunk with a branch reaching out in +x, -x, +z and -z at half height, ~38 logs total, mined at the
# base. Each run (trunk, each branch) gets its own "+" cross of leaves around it, same as the small/tall trunks above.
fill ~19 ~1 ~20 ~19 ~10 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~21 ~1 ~20 ~21 ~10 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~1 ~19 ~20 ~10 ~19 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~1 ~21 ~20 ~10 ~21 minecraft:oak_leaves[persistent=false,distance=1]
setblock ~20 ~11 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~21 ~6 ~20 ~27 ~6 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~21 ~4 ~20 ~27 ~4 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~21 ~5 ~19 ~27 ~5 ~19 minecraft:oak_leaves[persistent=false,distance=1]
fill ~21 ~5 ~21 ~27 ~5 ~21 minecraft:oak_leaves[persistent=false,distance=1]
setblock ~28 ~5 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~13 ~6 ~20 ~19 ~6 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~13 ~4 ~20 ~19 ~4 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~13 ~5 ~19 ~19 ~5 ~19 minecraft:oak_leaves[persistent=false,distance=1]
fill ~13 ~5 ~21 ~19 ~5 ~21 minecraft:oak_leaves[persistent=false,distance=1]
setblock ~12 ~5 ~20 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~6 ~21 ~20 ~6 ~27 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~4 ~21 ~20 ~4 ~27 minecraft:oak_leaves[persistent=false,distance=1]
fill ~19 ~5 ~21 ~19 ~5 ~27 minecraft:oak_leaves[persistent=false,distance=1]
fill ~21 ~5 ~21 ~21 ~5 ~27 minecraft:oak_leaves[persistent=false,distance=1]
setblock ~20 ~5 ~28 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~6 ~13 ~20 ~6 ~19 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~4 ~13 ~20 ~4 ~19 minecraft:oak_leaves[persistent=false,distance=1]
fill ~19 ~5 ~13 ~19 ~5 ~19 minecraft:oak_leaves[persistent=false,distance=1]
fill ~21 ~5 ~13 ~21 ~5 ~19 minecraft:oak_leaves[persistent=false,distance=1]
setblock ~20 ~5 ~12 minecraft:oak_leaves[persistent=false,distance=1]
fill ~20 ~1 ~20 ~20 ~10 ~20 minecraft:oak_log[axis=y]
fill ~21 ~5 ~20 ~27 ~5 ~20 minecraft:oak_log[axis=x]
fill ~13 ~5 ~20 ~19 ~5 ~20 minecraft:oak_log[axis=x]
fill ~20 ~5 ~21 ~20 ~5 ~27 minecraft:oak_log[axis=z]
fill ~20 ~5 ~13 ~20 ~5 ~19 minecraft:oak_log[axis=z]
assert block ~20 ~1 ~20 minecraft:oak_log
dummy bench_t mine ~20 ~1 ~20
await not block ~20 ~1 ~20 #minecraft:logs
await delay 5t
await not entity @e[type=minecraft:marker,dx=39,dy=39,dz=39]
data modify storage timber_test:tmp test set value "benchmark_bushy"
execute store result storage timber_test:tmp log_probes int 1 run scoreboard players get log_probes timber_bench
execute store result storage timber_test:tmp leaf_search_steps int 1 run scoreboard players get leaf_search_steps timber_bench
function timber_test:report_benchmark with storage timber_test:tmp
function timber_test:assert_no_tree {test:"benchmark_bushy"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#> With plant_sapling on, planting a sapling from a stack of more than one plants it and removes just one from the stack (the loop checks every 2 seconds)
# @environment timber_test:plant_sapling_on
# @template timber_test:arena
# @timeout 400
# @skyaccess true

function timber_test:platform
summon minecraft:item ~10.5 ~0.5 ~10.5 {Item:{id:"minecraft:oak_sapling",count:5}}

# the sapling item falls on the dirt, the loop runs every 2 seconds
await block ~10 ~ ~10 minecraft:oak_sapling
assert entity @e[type=minecraft:item,dx=39,dy=39,dz=39,nbt={Item:{id:"minecraft:oak_sapling",count:4}}]
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#> With plant_sapling on a sapling item that lies on the ground is planted (the loop checks every 2 seconds)
#> With plant_sapling on, planting the last sapling of a stack plants it and removes the item entity entirely (the loop checks every 2 seconds)
# @environment timber_test:plant_sapling_on
# @template timber_test:arena
# @timeout 400
Expand All @@ -9,3 +9,4 @@ summon minecraft:item ~10.5 ~0.5 ~10.5 {Item:{id:"minecraft:oak_sapling",count:1

# the sapling item falls on the dirt, the loop runs every 2 seconds
await block ~10 ~ ~10 minecraft:oak_sapling
assert not entity @e[type=minecraft:item,dx=39,dy=39,dz=39,nbt={Item:{id:"minecraft:oak_sapling"}}]
5 changes: 5 additions & 0 deletions tests/data/timber_test/test_environment/benchmark.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "minecraft:function",
"setup": "timber_test:env/group_setup",
"teardown": "timber_test:env/group_teardown"
}
Loading