Phase 9: reproducible builds, balance from the server, spark check, play feedback

Build: every tool pinned (JDK, packwiz, Gradle plugins, Python packages), exports
normalised, tools/check_reproducible.py proves two clean builds are byte-identical.

Server-side balance: a server can load config/cobblesurvival/datapack.zip in place of
the jar's data, /reload applies it, tools/server_update.py packages it and refuses
when a client change is needed. JEI drop pages are sent by the server. Code-held
balance numbers moved to tuning/server.json.

Performance: spark in the pack (server only), tools/perf_test.py with fake players
whose chunks really are active, docs/performance.md.

Adrian's feedback: default keys R, U, J, §; drop pages link to Cobblemon Info and show
real chances; bosses only hurt in battles; battle XP halved; channels between the
islands low level; temperature ×3 with insulation doubled; hazards start at once; no
item dots on the map; a wild attack starts a battle; the starter only faints; quest
book on join with a basics page and an HMs page; regional forms obtainable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
claudeandClaude Opus 5.5 committed 2026-11-09 16:26:30 +01:00
1 parent 0741c84077
commit 4083791944
133 files changed
+8350 -3273

No files matched your search

+16 -4
View File
@@ -81,8 +81,8 @@ A zone decides **which Pokémon spawn** and **at what levels**: Cobblemon's own
| Highland Meadows | north-east around the peaks (20 %) | plains, forest, cherry grove | 25–35 | none | leather (Ponyta) for heat insulation, Hard Stone (Larvitar) | Rapidash, Lv 40 |
| Sunscorch | the desert and badlands pockets: north of the ring channel, the north-east and north-west coasts (3.4 %) | desert, badlands | 30–45 | **heat** (Cold Sweat): heat insulation, Fire Resistance potions | armadillo scutes (Sandshrew), Soft Sand, magma cream (Numel) | Hippowdon, Lv 50 |
| Frostpeak Range | the jagged peaks plus the windswept hills and cherry groves right next to them (6 %) | jagged peaks | 40–55 | **cold** (Cold Sweat): wool insulation; warming food and potions | Never-Melt Ice (Snover), Razor Claw (Sneasel) | Abomasnow, Lv 60 |
| Shallows | coast and beaches | ocean, lukewarm and warm ocean, beach | 5–20 | none | fish, kelp | none |
| Deep Sea | ring and band channels, outer sea | deep ocean | 30–50 | none | Heart Scale-type rarities | Lapras or Wailord, Lv 55 |
| Shallows | coast, beaches and the channels between the islands | ocean within 1,500 blocks, beach | 5–20 | none | fish, kelp | none |
| Deep Sea | the sea surrounding the island (Adrian, 2026-11-09: the ring and band channels moved to the Shallows) | ocean beyond 1,500 blocks | 30–50 | none | Heart Scale-type rarities | Lapras or Wailord, Lv 55 |
| Lush Caves | under the whole island | lush caves | 12–28 | none | glow berries, mushrooms (Paras) | none |
| Dripstone Depths | under the whole island | dripstone caves | 22–38 | none | Dragon Fang (Bagon, Gible) | none |
| Deep Dark | under the cold and temperate land, deep (since 2026-10-30) | deep dark | 45–60 | darkness (vanilla sculk; no Warden) | | Sableye or Spiritomb, Lv 62 |
@@ -306,7 +306,7 @@ Adrian's choices: Cobblemon tasks come from **Cobblemon Quests Reloaded** (a mai
**The quest book** is written in `docs/quests.yaml` and generated by `tools/quests.py` (run by `tools/build.py`) into `pack/config/ftbquests/quests/` in FTB Quests 2101's format (`data.snbt`, `chapter_groups.snbt`, `chapters/*.snbt`, every title and text in `lang/en_us.snbt`). Chapter, quest and task ids are derived from their names in the YAML, so they stay the same across regenerations and players keep their progress. The whole book is in flexible progression with lock icons off: dependencies show as lines but never block. No rewards.
- **Chapters** (18, 74 quests on 2026-11-03): Getting Started (welcome, first catch, Poké Balls, a full team, the Revival Charm and permadeath, healing, level 10, drops and JEI, evolution, on to Southwold); one per zone and the End; Pokémon Machines (how to build one, then each of the five); Base and Jobs (Pasture Block, PC). A zone chapter (`zone: <id>`) starts with the template quests `zone_chapter`, filled from the zone's data: reach it (levels, hazard), catch three of its Pokémon (any species of its spawn pool), mine its ores (one item task per vein), beat its guardian (species, level, drops, the boss bar). Chapters add their own quests (Mire Waders for the Mire; Eyes of Ender and Eternatus for the End).
- **Chapters** (20 with the HMs page of 2026-11-09, listing all 39 field moves): Getting Started (welcome, keys, the starter, battling, first catch, Poké Balls, a full team, the Revival Charm and permadeath, healing, level 10, drops and JEI, evolution, on to Southwold); one per zone and the End; Pokémon Machines (how to build one, then each of the five); Base and Jobs (Pasture Block, PC). A zone chapter (`zone: <id>`) starts with the template quests `zone_chapter`, filled from the zone's data: reach it (levels, hazard), catch three of its Pokémon (any species of its spawn pool), mine its ores (one item task per vein), beat its guardian (species, level, drops, the boss bar). Chapters add their own quests (Mire Waders for the Mire; Eyes of Ender and Eternatus for the End).
- **Tasks:** Cobblemon actions through Quests Reloaded (catch, defeat, evolve, level up), items, and the pack's own events: hidden advancements `cobblesurvival:events/zone/<zone>`, `guardian/<zone>`, `machine/<core>` and `end_boss` (no display, no toast; written by `tools/quests.py`), granted by the companion mod (`QuestEvents`: a player's zone once a second, a guardian's battle winners, a machine's owner when a Pokémon goes in, the End boss's winners). They unlock nothing.
- **Node positions:** Adrian moves nodes in the in-game editor; `tools/quests.py --pull <folder>` records every node that isn't where the generator would put it in the `layout` block at the end of `docs/quests.yaml`. `tools/prism_instance.py` does that from the instance before each sync, and rebuilds if something moved, so a sync never undoes his layout (docs/quests.yaml then needs committing). Titles, texts, tasks and dependencies edited in game are overwritten.
- **Self-tests:** `quest_book_loads` (FTB Quests on the server has the book in flexible mode, every task type is known, every item and advancement exists), `quest_events_mark_zones`, and the guardian and furnace tests check their events.
@@ -320,7 +320,7 @@ Adrian's choices: Cobblemon tasks come from **Cobblemon Quests Reloaded** (a mai
The pages sit next to Cobblemon Info's own, and Adrian likes having both, so they share its look (2026-11-09): the same dark panel with a thin border, a header with icon, white title, warm tag on the right and a divider, the same text colours and spacing. Long text wraps. JEI gives all pages of a category the height of the tallest.
JEI runs on the client, which on a dedicated server doesn't have the server's data, so the pages are built from `assets/cobblesurvival/jei/drop_sources.json` inside the mod, written by `tools/jei_sources.py` from the pack's own files (Cobblemon's species drops with the pack's species additions, the zone spawn pools, guardians, the End boss, the machines, `world/zones.json` ores, Cobblebase's zone tables). Which ore a vein gives now lives in `world/zones.json` → `ores` → `item` (the quest book reads it too). Self-tests: `drop_sources_match_the_server` compares the file with the live server (every listed Pokémon's drops and zones, spawns with own drops, guardians, the End boss, altar tiers) and fails when it's stale; `jei_pages_build` builds every page with the same code JEI calls and checks every item exists.
JEI runs on the client, which on a dedicated server doesn't have the server's data, so the server sends the pages' data to each player on joining and after `/reload`: `data/cobblesurvival/drop_sources/pages.json`, written by `tools/jei_sources.py` from the pack's own files (Cobblemon's species drops with the pack's species additions, the zone spawn pools, guardians, the End boss, the machines, `world/zones.json` ores, Cobblebase's zone tables). Which ore a vein gives now lives in `world/zones.json` → `ores` → `item` (the quest book reads it too). Self-tests: `drop_sources_match_the_server` compares the file with the live server (every listed Pokémon's drops and zones, spawns with own drops, guardians, the End boss, altar tiers) and fails when it's stale; `jei_pages_build` builds every page with the same code JEI calls and checks every item exists.
## Phase 9: balancing and release (Adrian, 2026-11-09)
@@ -341,6 +341,18 @@ Self-tests: `every_implemented_pokemon_is_obtainable` (every implemented species
**The respawn block** (Regenerating Ore Veins' regenerator, the placeholder where a mined vein block grows back) no longer stops the player's aim, so the ore behind it can be mined; it never had collision. While sneaking the player can still target it, which is how its info opens. A mixin returns an empty outline shape (`RegeneratorBlockMixin.java`, the mod's only one, targeting the class by name). Its model and glass come from `pack/kubejs/assets/regenerating_ore_veins/`, much fainter than the stained glass it had. Self-test `regenerator_lets_the_aim_through` casts a real ray at it.
### Drop chances on the JEI pages (Adrian, 2026-11-09)
The pages first showed each table's raw percentage, which isn't what a player gets. Cobblemon's `DropTable.getDrops` makes `amount` picks; in each it walks the table in order and takes the first entry that passes its percentage, so later entries are rarer than written. A count of 0 gives nothing. `tools/jei_sources.py` now works out the real chance of getting at least one, and the self-test `drop_chances_match_cobblemon` rolls Cobblemon's own code 4,000 times for every Pokémon and compares. The Pokémon in a page's corner is Cobblemon Info's own JEI ingredient, so clicking it opens that mod's pages.
### Balancing without a client update (Adrian, 2026-11-09)
Playtesters shouldn't have to reinstall for a number change. The server is the authority for everything that isn't drawn: Cobblemon, recipes, quests and Cold Sweat send clients what they need, and the pack's data pack now does too. A server can hold `config/cobblesurvival/datapack.zip`, which the companion mod loads in place of the copy in its jar, under the same pack id, so a world keeps it enabled. One file, so nothing left over from an older version; `/reload` applies it.
The JEI pages were the one thing clients took from the jar. Their data moved into the data pack; the server gzips it and sends it on joining and on `/reload` (`DropSources`). JEI can start before or after that packet, so the plugin both builds from what has arrived and swaps pages in the running JEI (`hideRecipes` and `addRecipes`). Numbers that only lived in Kotlin moved to `data/cobblesurvival/tuning/server.json` (`Tuning`).
`tools/server_update.py` packages this. It hashes what clients run or draw against `tools/client-baseline.json`, the last pack handed out, and refuses when they differ, rather than letting players find out from a crash or a missing texture.
## Open decisions for Adrian (proceeding with the default, flagged)
| Decision | Default used for now |