- JavaScript 67.4%
- Python 32.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
BandwidthOptimizer: 1.21.1-5.10.30.134-neoforge -> 1.21.1-5.10.32.139-neoforge OpenComputers II: MC1.21.1-neoforge-0.9.0 -> MC1.21.1-neoforge-0.9.1 Polytone: 1.21-5.0.0 -> 1.21-5.0.1 Not released. pack.toml is untouched and the jars still want testing in game. |
||
| .forgejo/workflows | ||
| assets | ||
| overrides/configureddefaults | ||
| scripts | ||
| theme | ||
| .gitignore | ||
| CHANGELOG.md | ||
| LICENSE | ||
| MODRINTH.md | ||
| modrinth.toml | ||
| mods.lock.json | ||
| pack.toml | ||
| README.md | ||
![[balls_game_2]](/kiki/balls-game-2/media/branch/main/assets/front.png)
balls-game-2
[balls_game_2] is a private modpack made for my friends and I. Minecraft 1.21.1,
NeoForge, 132 mods.
Built on the same FOSS-first philosophy as its predecessor, it blends industry, chemistry and physics-driven mechanics across a carefully selected mod list.
Major content
Create for complex mechanical automation, with addons spanning armed airships (Create Aeronautics, Create Big Cannons), rockets and two off-world dimensions (Create Cosmonautics), cooking (Central Kitchen) and enchanting (Enchantment Industry).
The Factory Must Grow, Chemica and Create: Power Grid are the industrial spine. Oil extraction and distillation, chemical vats, electrolysis, the acids, the Kroll titanium process, and circuit fabrication thirteen recipe layers deep.
Sable for fully physics-simulated vehicles with realistic momentum and collision, plus Drive-By-Sable for the controls.
Applied Energistics 2 for late-game digital storage and autocrafting, extended by ExtendedAE, MEGA Cells and ME Requester.
Computing in three eras that chain into each other. TIS-3D is assembly written by hand on four-port nodes. CC: Tweaked is Lua, and holds the actuators: vehicles through CC: Sable, cannons through CC:CBC, Create through the CC:C Bridge, the power grid through CCPowerGridCompat. OpenComputers II emulates RISC-V and Z80 and runs real Linux. A TIS-3D serial port reaches an OC2 machine at 300 baud, and OC2 presents itself to CC as a peripheral.
Eternal Starlight as a second dimension reached by rocket, and Re:Avaritia for the Infinity endgame that runs through it.
Starcatcher, Garnished and Farmer's Delight for fishing, cooking and a shop to sell it through. Exposure adds cameras and film developing.
Epic Terrain generates the world. Caxton provides custom font support and Sodium carries performance.
How this repo works
This repo is the source of truth for the pack. It holds no mod jars: mods.lock.json
records which Modrinth version of each mod the pack pins, and the launcher fetches
them at install time.
Layout
pack.toml pack name, version, Minecraft and loader versions, Java hints
mods.lock.json 129 pinned mods: project id, version id, URL, sha1, sha512, env
modrinth.toml Modrinth-only release settings
overrides/ everything that ships to players, under configureddefaults/
theme/ quest book theme, injected into the resource pack
CHANGELOG.md release notes, one H2 section per version
scripts/sync.py pull the live instance into this repo
scripts/update.py check Modrinth for newer versions of the pinned mods
scripts/gen_ftbquests.py regenerate the quest book from the advancement tree
scripts/apply_theme.py write theme/ into the shipped resource pack
scripts/build.py dist/*.mrpack and dist/*-instance.zip
scripts/publish.py upload a version to Modrinth
scripts/check_ids.py every item id the pack names, against the installed jars
scripts/tech_tree.py item dependency graph, read out of the installed jars
scripts/resync_chemica_overrides.py rebuild the chemica overrides from its jar
scripts/upload_jars.py push the non-distributable jars to the package registry
Everything the pack ships lives under overrides/configureddefaults/, with no
exceptions. The ConfiguredDefaults mod copies a file out only where the player
does not already have one, so updating the pack can never overwrite someone's
edited config.
That applies to the quest book too. It means a player who has already launched
the pack keeps the book they have and will not receive a newer one, since the
file is already there. Only a fresh install gets the current book. Anyone wanting
the new one deletes config/ftbquests/quests/ and relaunches; progress lives in
saves/<world>/ftbquests/ and is not affected.
Working on it
Edit the live instance, then pull the changes in:
scripts/sync.py # mods and overrides
scripts/sync.py --files # overrides only, no Modrinth calls
sync.py hashes every jar, resolves it through Modrinth's file-hash lookup, and
rewrites the lockfile. A jar that is not on Modrinth is reported rather than
silently dropped, because it cannot legally or mechanically go in the manifest.
Keeping mods current
scripts/update.py asks Modrinth's bulk update endpoint whether any pinned mod has
a newer build that still matches the pack's loader and Minecraft version, so it can
never suggest a jar that would not load.
scripts/update.py # report; exit 10 if anything is stale, 0 if not
scripts/update.py --apply # rewrite mods.lock.json
scripts/update.py --json # machine-readable
bump-mods.yml runs it daily, applies what it finds, rebuilds to prove the lockfile
still produces a valid manifest, commits, and posts to Discord. It never touches
pack.toml: a mod bump is not a release, and the jars still want testing in game.
Hold a mod back by adding its slug to PINNED in scripts/update.py with the
reason. Held mods are reported but never applied.
The quest book
scripts/gen_ftbquests.py builds all 188 quests, in 13 chapters, from the
advancement tree under
overrides/configureddefaults/kubejs/data/balls_game/advancement/balls, so the
book and the advancements cannot drift apart. An inventory_changed criterion
becomes an item task, changed_dimension becomes a dimension task, parent
links become dependencies, and the advancement frame picks the quest shape and
the xp reward.
Titles and descriptions go in lang/en_us.snbt keyed by id, not in the chapter
files, which is how FTB Quests itself writes them.
scripts/gen_ftbquests.py # rewrites overrides/configureddefaults/config/ftbquests/quests/
scripts/apply_theme.py # theme/ into the shipped resource pack
Regenerating does not wipe anything
FTB Quests assigns a fresh random id to every quest and chapter it rewrites, so ids cannot be used to match a regenerated book against the one on disk. The generator reads the old book back first and matches on what FTB Quests leaves alone:
- a quest, by the item its first task asks for
- a chapter, by its filename
Anything matched keeps its id, its x and y, and a title that was edited by hand.
So the book can be laid out and renamed in game, regenerated afterwards, and
neither the positions nor player progress in saves/<world>/ftbquests/ is lost.
New quests get ids and fall into free slots.
Editing the book in game is fine; sync.py --files pulls it back. Regenerate
after syncing, not before, since the generator overwrites the whole directory and
can only preserve what it finds there.
Theme
theme/ftb_quests_theme.txt is the pack's theme. FTB Quests loads
ftbquests:ftb_quests_theme.txt through getResourceStack, so every resource
pack that provides the path stacks over the mod's own default and the file only
carries the properties that change. apply_theme.py validates it and writes it
into overrides/configureddefaults/resourcepacks/[balls_game].zip as
assets/ftbquests/ftb_quests_theme.txt.
It is kept outside overrides/ because anything under there also ships as a
loose file, and a loose theme/ in the mrpack does nothing but confuse.
Overriding another mod's recipes
The pack overrides 138 of Chemica's recipes, because Chemica writes camelCase keys and names the vat type ids TFMG renamed in 1.3.1. An override freezes the upstream recipe at whatever version it was copied from, so a Chemica update silently reverts to the old numbers until the overrides are rebuilt.
scripts/resync_chemica_overrides.py
It takes Chemica's current file, applies only the two mechanical fixes, and keeps
the pack's own file wherever there was a deliberate content decision, printing
ADOPT or KEPT with the reason for each. Running it against a Chemica it has
already been run against produces no diff, which is the check that it is still in
sync.
scripts/tech_tree.py reads every recipe in every jar, including JarJar nests,
plus the pack's own overrides, resolves tags and walks outward from what the world
gives for free. Used to confirm nothing in the progression tree is unreachable.
scripts/tech_tree.py depth <item> cheapest full route to one item
scripts/tech_tree.py tier every item grouped by depth
scripts/tech_tree.py chain <ns> depth-ordered milestones for one namespace
Releasing
Bump version in pack.toml, add a ## <version> section to CHANGELOG.md, then:
scripts/build.py
scripts/publish.py --dry-run
git tag 2.0.0-beta5 && git push origin 2.0.0-beta5
The tag triggers release.yml, which checks the tag against pack.toml, builds,
uploads to Modrinth, creates the Forgejo release and attaches both artifacts, then
posts a Discord embed.
Workflows
| file | trigger | does |
|---|---|---|
bump-mods.yml |
daily at 06:23, or manual | check Modrinth, apply, rebuild, commit |
release.yml |
tag push, or manual | build, Modrinth upload, Forgejo release, notify |
Both take optional secrets and skip cleanly when one is missing rather than failing:
MODRINTH_TOKEN:pat_token with "Create versions"DISCORD_WEBHOOK: release and bump notifications
FORGEJO_TOKEN is provided by the runner; nothing needs setting for the git push
or the release API.
Artifacts
Some mods in the pack are All Rights Reserved and absent from Modrinth, so they
cannot go in a Modrinth manifest: there is no allowlisted URL to point at, and no
permission to bundle the jar. FTB Quests, FTB Library and FTB Teams are the current
three. build.py therefore produces two packs rather than one.
| file | mods | where it goes |
|---|---|---|
<slug>-<version>.mrpack |
124 referenced | Modrinth. Omits the three. |
<slug>-<version>-full.mrpack |
124 referenced + 3 bundled | Forgejo releases only. The complete pack. |
<slug>-<version>-instance.zip |
none | Configs over an existing install. |
A jar joins the bundled set when sync.py cannot find it on Modrinth by file hash.
It is recorded under unresolved in mods.lock.json, so nothing vanishes quietly,
and build.py names which mods each variant drops or carries.
publish.py refuses outright to upload anything with -full in the name, and
modrinth.toml does not glob for it either. Both guards, because the consequence
Modrinth states for redistributing without permission is takedown plus account
removal.
Build one variant at a time with scripts/build.py --variant modrinth|full|instance.
The instance skeleton never carries jars. Bundling all 127 would mean redistributing every one, which most of their licences do not grant.
Java
ZGC is worth setting; its pauses are sub-millisecond where G1's are not. Generational
ZGC has been the default since JDK 23, so -XX:+UseZGC is the whole flag.
-XX:+ZGenerational was removed in JDK 24 and will refuse to start.
-XX:+UseZGC -XX:+AlwaysPreTouch with equal min and max heap
A modpack manifest cannot set JVM arguments, so this is on the player. The
-instance.zip sets it for you.