apply_jvm wrote instance.cfg and never restored it, so B5-B12a in run 1 ran on B4's last set (Shenandoah, 6 GB). Within-block verdicts hold (same JVM on both sides); B7's no-shader baseline is re-run in run 2. Also: B14 spawn-count automation (b14/: fresh seeded world per run, offline-located biome sites, datapack entity counts, journaled config variants) and RESULTS-proxy.md with run-1 verdicts.
170 lines
9.5 KiB
Markdown
170 lines
9.5 KiB
Markdown
# B14: spawn-tuning measurement
|
||
|
||
`b14.py` automates SPAWN-TUNING.md §B14. It compares the upstream spawn defaults with the
|
||
shipped partial files (`d80f85b`, `8e8483d`: `alexsmobs-common.toml`, `fowlplay.json5`,
|
||
`crittersandcompanions-common.json5`). Each comparison stands still for 10 minutes at a
|
||
river, a jungle and a warm ocean, and counts entities within 128 blocks.
|
||
|
||
It reuses `../bench.py` as a module: `launch`, `kill_client`, `LogTail`, `monitor`, the
|
||
`Journal` / `restore_pending` crash-safe restore, `apply_pins`, `apply_dh`, and
|
||
`zip_world_root`. It needs nothing beyond the Python 3 standard library.
|
||
|
||
## Loop
|
||
|
||
```sh
|
||
cd analysis/client-perf/harness
|
||
python3 b14/b14.py run --config defaults --dry-run # world, config plan, datapack
|
||
python3 b14/b14.py run --config defaults # ~32 min, all three biomes
|
||
python3 b14/b14.py run --config shipped
|
||
python3 b14/b14.py summary # SPAWN-TUNING §B14 verdict
|
||
```
|
||
|
||
- `--biome river,jungle,warm_ocean` (default `all`) runs the sites in that order in one
|
||
launch.
|
||
- `--minutes 10` (default) sets the stay at each site.
|
||
- Repeating runs (for example two of each config, interleaved
|
||
defaults → shipped → defaults → shipped) gives medians. The summary takes the median
|
||
across runs.
|
||
- Results go to `~/onion-bench/results/b14.jsonl`. Each count line is a `b14_count` row,
|
||
and each launch is a `b14_session` row (outcome, config check, git/mods hash, sites,
|
||
archived log at `results/logs/session-<sid>.log`).
|
||
- `b14.py restore` (or `bench.py restore`, which uses the same journal) undoes a
|
||
hard-killed run.
|
||
- `b14.py parse LOG` prints the count rows found in any archived log.
|
||
|
||
The script refuses to start in these cases:
|
||
- a restore journal is pending;
|
||
- a bench client is running;
|
||
- any `matrix.py run`, `bench.py session` or other `b14.py run` process exists. It shares
|
||
the OnionBench instance with the matrix, so **never run it while the matrix runs.**
|
||
|
||
## Design
|
||
|
||
### Throwaway world `saves/onionspawn`, one per run
|
||
- It contains only the bench world's `level.dat` (seed `onionbench`, gamerules, datapack
|
||
list), the `onion-bench` datapack (it has no load or tick tags) and `serverconfig/`.
|
||
- It has no `region/`, `entities/` or `data/` (no forceloads, no DH database). Every chunk
|
||
around a site is therefore generated during the run, under the config being measured.
|
||
That covers both kinds of spawning: the initial creatures placed at world generation
|
||
and natural spawning.
|
||
- The seed is the same, so the terrain is identical between runs. The world is journaled
|
||
as a `tree` entry and deleted on restore. The bench world `saves/onionbench` is never
|
||
touched.
|
||
|
||
### Sites are pre-located offline (`b14.py locate`)
|
||
`/locate biome` can't be used from a datapack. Functions run with suppressed output, and
|
||
`execute store` only captures the distance, not the coordinates.
|
||
|
||
So `locate` parses the overworld region files in `bench-world-v1.zip` with a small NBT
|
||
reader (`nbt.py`):
|
||
- It reads each chunk's biome palette at the surface, using the `MOTION_BLOCKING_NO_LEAVES`
|
||
heightmap in full chunks and `WORLD_SURFACE_WG` in proto chunks.
|
||
- For each target biome it picks the chunk whose 5×5 neighbourhood has the largest share
|
||
of that biome.
|
||
- Sites must be at least 192 blocks from the world spawn and the saved player position
|
||
(spawn chunks and the first-load area stay outside the 128-block count sphere), and at
|
||
least 320 blocks from each other (the count spheres don't overlap).
|
||
|
||
Biomes depend only on the seed, so the coordinates are valid for any world with this seed
|
||
and mod set. They are committed in `sites.json` (from zip sha256
|
||
`144caf5f…`, seed −306676217):
|
||
|
||
| biome | x y z | biome at site | share in 5×5 chunks |
|
||
|---|---|---|---|
|
||
| river | 904 63 88 | minecraft:river | 0.37 |
|
||
| jungle | −8 107 −201 | minecraft:jungle | 0.52 |
|
||
| warm_ocean | −200 63 56 | minecraft:warm_ocean | 0.75 |
|
||
|
||
Every count line carries `biome_ok`, an in-game `execute if biome` check at the player's
|
||
feet (`minecraft:river`, `#minecraft:is_jungle`, `minecraft:warm_ocean`). If the
|
||
regenerated terrain doesn't match the offline lookup, the summary marks that biome
|
||
invalid instead of silently measuring the wrong place.
|
||
|
||
### Config variants (journaled, bench instance only)
|
||
- `defaults` deletes the three files. Each mod regenerates its upstream defaults on load
|
||
(the same as "these three config files removed" in SPAWN-TUNING). They aren't in
|
||
`config/defaultoptions/extra/`, so Default Options can't put them back.
|
||
- `shipped` writes `git show <rev>:config/<file>` (default `HEAD`), meaning the committed
|
||
partials, not the working tree's boot re-saves.
|
||
- After the run, and before the restore, the files the mods re-saved are read back and
|
||
checked against the expected values. A mismatch fails the run (`config_check.ok=false`),
|
||
and the summary ignores that run. The expected upstream defaults were verified from the
|
||
jars:
|
||
- Alex's Mobs: the `# Default:` comments in its re-saved TOML: gazelle 40, capuchin 28,
|
||
elephant 30, straddler 70, rocky roller 60, underminer 50, blobfish 30,
|
||
mudskipper 28, caiman 29.
|
||
- Fowl Play: `FPConfig.<init>` bytecode: duck 4/max 6, goose 4/4, gull 5/8,
|
||
sparrow 20/6.
|
||
- Critters and Companions: `CACCommonConfig$SpawningConfig.<init>`: sea bunny warm
|
||
ocean 32 (max 4), other oceans 16; leaf insect jungle 14, forest 6; dragonfly lush 10.
|
||
|
||
### Datapack (`saves/onionspawn/datapacks/b14-autoplan`, pack_format 48)
|
||
- **Start-up:** a load tag polls once a second until a player exists. Then it pins the
|
||
rules that matter for spawning:
|
||
- `gamemode creative` (it counts for spawning and despawning, where spectators don't,
|
||
and the player can't die);
|
||
- `doMobSpawning true`;
|
||
- the daylight, weather, patrol, trader, insomnia and warden rules;
|
||
- time 6000 and clear weather.
|
||
- **Per site:**
|
||
1. Teleport above the site.
|
||
2. 5 s later: clear a 3×3×3 pocket, put a barrier at y−1 (a platform over water), and
|
||
teleport onto it.
|
||
3. Count at t=0 (+10 s), then every 60 s up to t=10. Each count re-asserts the barrier
|
||
and the teleport, so the player never drifts.
|
||
- **Each count** runs `execute store result score #<alias> b14 if entity
|
||
@e[type=<id>,distance=..128]` as the player, for the 22 tuned species and 10 vanilla
|
||
spot-check species. It also stores:
|
||
- `total` = `@e[type=!player,distance=..128]`, as SPAWN-TUNING defines it;
|
||
- `nonitem`, which also excludes items and XP orbs;
|
||
- `tuned`, the sum of the tuned species.
|
||
- **Output:** a `tellraw` prints one line, which the client logs as `[System] [CHAT]`:
|
||
`B14 <sid> <biome> t=<min> biome_ok=1 total=N nonitem=N tuned=N alexsmobs:gazelle=N ...`.
|
||
- **Errors:** `Failed to load function b14:` in the log (an unknown entity id makes the
|
||
whole function unloadable) aborts the run as `datapack_error`.
|
||
|
||
### Entity ids
|
||
All are checked against the jars in the bench instance. Each has an `entity.<mod>.<name>`
|
||
lang key, and is either in the mod's registry class strings (`CACEntities`) or has a
|
||
`loot_table/entities/<name>.json`.
|
||
- **Alex's Mobs** (`alexsmobs-1.22.17`): `gazelle`, `capuchin_monkey`, `elephant`,
|
||
`straddler`, `rocky_roller`, `underminer`, `blobfish`, `mudskipper`, `caiman`.
|
||
- **Fowl Play** (`fowlplay-1.2.3`): `duck`, `goose`, `gull`, `sparrow`.
|
||
- **Critters and Companions** (`crittersandcompanions 2.7.0`): `leaf_insect`, `sea_bunny`,
|
||
`dragonfly`, `ladybug`, `stag_beetle`, `roly_poly`, `snail`, `stick_bug`, `weevil`.
|
||
|
||
### Verdict (`summary`, SPAWN-TUNING §B14)
|
||
- **Per biome**, using the final count (t = minutes):
|
||
- **keep** if the median `total` drops under shipped *and* no tuned species that
|
||
appeared at any minute under defaults is absent under shipped;
|
||
- **back off** (listing the species) if one disappears;
|
||
- **no drop** otherwise.
|
||
- **Warnings:** `rare_warn` flags species whose shipped maximum is under 25 % of the
|
||
defaults maximum (when defaults saw at least 4). `vanilla_gone` flags cows or sheep
|
||
that vanished.
|
||
- **Overall:** `keep` only if all three biomes keep.
|
||
- **Excluded runs:** runs with a failed config check or a non-ok outcome are ignored. A
|
||
biome with `biome_ok != 1` is `invalid`.
|
||
|
||
## Manual prep and limits
|
||
- **Prep:** nothing beyond what's committed. `sites.json` is already generated. Re-run
|
||
`b14.py locate --out b14/sites.json` only if the seed, the source zip or the
|
||
biome-affecting mods (Terralith, Tectonic) change.
|
||
- **Thin biome coverage:** the source world has only about 2300 chunks with final biomes,
|
||
so the river and jungle patches are small (37 % and 52 % of the 5×5 neighbourhood). The
|
||
128-block count sphere also covers neighbouring biomes. This is the same for both
|
||
configs, but it lowers the absolute counts.
|
||
- **Better sites (optional):** on `~/onion-bench/server-bench` (seed `onionbench`), run
|
||
`/locate biome minecraft:river` (and jungle, warm_ocean), check the share around each
|
||
result, and put the coordinates into `sites.json` by hand. The `y` value is the first
|
||
free block above the surface or the water.
|
||
- **Jungle site from a proto chunk:** its `y` is the height before trees are placed. The
|
||
3×3×3 air pocket keeps a tree from burying the player, and `biome_ok` still guards the
|
||
biome.
|
||
- **Hostile mobs in `total`:** difficulty stays as in `level.dat` (normal), so hostiles
|
||
from caves within 128 blocks count toward `total`. `tuned` and `nonitem` are recorded
|
||
separately for reading the result.
|
||
- **Machine:** this is proxy-machine data (singleplayer, simulation distance 8 pinned).
|
||
Spawning is server logic, so the GPU doesn't matter, but CPU load does affect chunk
|
||
generation speed.
|