Repository navigation
Development
Building and testing Legacy Connector. Requires JDK 21 (newer JDKs fail with "Unsupported class file major version"). Back to Home.
Requires JDK 21.
./gradlew build # runtime mod + loader (also builds the distributable jar)
./gradlew bundleJar # single distributable jar: build/dist/legacyconnector-<ver>.jar, the ONLY jar for a mods folder
# (build/libs/*-runtime-only.jar has no loader: every legacy mod is then rejected as built for an old Minecraft)
./gradlew runClient # dev client, mods from run/mods
./gradlew runServerTest # headless smoke test with run-servertest/mods; the fixture mod self-tests and stops
LEVELTYPE=biomesoplenty:biomesop tools/dev/run-subset.sh biomesoplenty # same, in a mod's world type
tools/dev/run-subset.sh hbm thaumcraft # server test over a few mods only, with the
# block/item/entity self tests; the whole corpus takes minutes
./gradlew runClientTest # client smoke test (run-clienttest, mods linked from run/mods): reports per mod
# which models show the missing texture, then quits
tools/dev/run-screenshot-test.sh agricraft appliedenergistics # graphical test: creates a world (WORLDTYPE=biomesoplenty:biomesop
# for BoP), builds block/item showcases, writes screenshots to run-screenshots/shots
SCENARIO=village tools/dev/run-screenshot-test.sh witchery agricraft # flies over the nearest village (legacy village components)
SCENARIO=gui MODS=rftools tools/dev/run-screenshot-test.sh rftools mcjtylib # right-clicks each block of the mods and
# screenshots the screens that open (custom GUIs); SCENARIO=portal tests a Twilight Forest portal
tools/dev/docker-headless.sh run-screenshot-test.sh ... # same, inside a Linux container with a virtual display
# (Xvfb + Mesa; needs `colima start`), so no window opens on the desktop;
# also works for run-client-test.sh
tools/dev/session.sh start <jar fragments> # one long-lived headless client you drive without restarting it:
tools/dev/session.sh send cmd "legacyconnector dim 7" # run a command / open GUIs / `mouse x y` / `click x y` / `useitem` / `inv` / `info` ...
tools/dev/session.sh shot name # screenshot -> ~/.cache/legacyconnector-docker/work/run-screenshots/control/out/name.png
tools/dev/survey.sh <namespace> <name> # places every block of a mod on a sky platform and screenshots it
./gradlew checkLegacyMod -PlegacyJar=/path/a.jar,/path/b.jar # e.g. every jar in test-mods/
# translate offline as one modset, list unlinkable references,
# generate + JVM-verify the synthesized API (no game launch)Development commands (operator, any world): /legacyconnector mods lists the loaded legacy mods;
/legacyconnector dim list|<id> [pos] switches to a legacy dimension; /legacyconnector gui <mod> <id> [pos] opens a mod
GUI by its IGuiHandler id; /legacyconnector place <block> [meta], give <item> [meta] [count] and spawn <entity> [count]
create legacy content with metadata; /legacyconnector list <blocks|items|entities> <mod> prints a mod's ids; /legacyconnector biome <id> jumps to a legacy biome; /legacyconnector inspect
describes the block or entity you look at (legacy class, metadata, tile entity, render type). Ids tab-complete.
Project layout:
| Path | What |
|---|---|
loader/ |
FML locator service: jar inspection, translation, linking, API synthesis, caching, report |
src/main/java/legacyconnector/v1_7_10/ |
1.7.10 shim (legacy package names, relocated) |
src/main/java/org/kylepitcock/legacyconnector/ |
runtime: bootstrap, bridges to NeoForge, JDK/library compat, synth runtime |
testmods/ |
fixture mod compiled against legacy API stubs, built like a real 2014 jar |
tools/ |
developer scripts: reference data download, signature/hierarchy extraction, API lookup, shim hierarchy audit |
tools/docker/, tools/dev/
|
headless-display container image; dev test scripts (subset server test, client/screenshot tests) |
test-mods/ |
(not committed) local corpus of real legacy mods for checkLegacyMod / runServerTest
|
Reference data (MCP mappings, Forge userdev sources, Mojang client jars) is downloaded into build/devtools/ by
tools/fetch_devtools.sh and the extraction scripts; none of it is committed. Only class names and supertypes
(hierarchy.txt) and MCP name tables are shipped.
To regenerate the 1.7.10 hierarchy data:
tools/fetch_devtools.sh
python3 tools/extract_legacy_signatures.py 1.7.10 # needs build/devtools/mojang/client-1.7.10.jar
python3 tools/extract_legacy_hierarchy.py 1.7.10-
./gradlew compileJava --rerunis the compile check (compileJava is non-incremental: the shim access widener rewrites class files), then./gradlew generateShimIndex, which catches "phantom field access" errors: shim code must read legacy fields that exist only as virtual fields through themcc$get$/mcc$set$accessors. -
tools/dev/run-corpus.sh [dir]runs the server self test over a whole directory of jars. Run only one server at a time and never compile while one runs. - Translation results are cached per jar and mod set (
run-*/.legacyconnector/cache). After changing the loader, linker, stubber or shim, delete the cache or the old translation is reused. - On a Windows desktop (no Docker)
tools/dev/quick-shots.shruns the graphical test directly in about a minute (superflat world, short render distance, a dozen blocks and items per mod); mods go inrun-screenshots/mods. - Headless server probing (no window):
./gradlew runServerSubTest -PautostopTicks=0 -PserverScript=probe -PserverScriptMod=<legacy mod id>runs a long-lived server that executes the*.jsfiles dropped intorun-subtest/probeon the server thread (Nashorn,core/ServerScript.java);tools/dev/server-script.sh -f file.jssubmits one and prints the answer (globals persist; a functiononTick(n)runs every tick).tools/dev/fisk/lib.jsis a library for it: it joins fake clients (a captured connection that answers keep-alives and summarises every packet the server sends, so data syncing and custom packets can be checked), puts a hero suit on, drives data vars and interactions and runs multi-tick scenarios (scenario([step, ...]), a step may return the ticks to wait;tools/dev/fisk/wait.shwaits and prints). Theprobe-*.jsand*-sweep.jsfiles there are the Fisk's Heroes ability checks (damage, beams, web, shield throw, sizes, gravity, healing, recipes, machines, items). A fake client never moves its chunk view by itself:tp()tells the chunk map.tools/dev/fisk/probe-client-keys.jsis the client-side counterpart (run withtools/dev/js.sh -fin a control session wearing the Mk50 chest piece): it records, per client tick, every change of the worn suit, the nanites data, sprint/sneak state, the ability keys and the key hint list the HUD draws, andFLK.report()prints them (the key hint flicker and the dead Iron Man blade/cannon of issue #118). - Live probing: start a session with
SCENARIO=control tools/dev/quick-shots.sh, thentools/dev/js.sh '<JavaScript>'evaluates in the running game (bindingsmc,server,log; any class viaJava.type(...)). - Headless server probing:
./gradlew runServerSubTest -PserverTickScript=<file.js> [-PserverTickScriptFrom=200]evaluates the file on every server tick (bindingsserver,tickcounted from the first run,log; globals persist, so place on one tick and read on the next); output is logged asSERVERSCRIPT:. Example: count a mod's blocks per height over the loaded overworld chunks.tools/dev/server-scripts/lib.js(prepend it:cat lib.js script.js > run.js) addsrealPlayer(name)(a real, non-fakeServerPlayeron an embedded channel, so mods that skip fake players keep progression data),placeTile(right-click placement with the item),P(label, fn)(catch + short stack) andlegacy(...)/lworld(...)wrappers;chromaticraft-*.jsthere are worked examples (research walk and a finished casting-table cast, dimension 60 stay, machines). -
tools/dev/thaumcraft/lib.js(fake player,scanAt= what the Thaumometer computes for a block) andscan-thaumometer.js(realItemThaumometer.doScanon placed blocks, PASS/FAIL per block) run on a-PserverScript=run-subtest/probe -PserverScriptMod=thaumcraftserver (submitlib.jsfirst; the directory is relative torun-subtest). Place blocks inside chunk 0,0 only: a block in an unloaded chunk makes the script block the server thread on chunk generation.SERVERSCRIPT:. Example: count a mod's blocks per height over the loaded overworld chunks. Ready-made gameplay rigs live intools/dev/server-scripts(lib.jsplaces items through a fake player'suseOn, forceloads the area and reads tiles/fields; ElectriCraft generator -> wire -> motor/battery/RF cable chains and ReactorCraft fission reactor, steam turbine -> shaft and radiation rigs). Gotchas:forceload addtakes block coordinates, not chunk coordinates (otherwise the rig unloads after one tick); legacy mods that kill entities outside y 0..256 (DragonAPI particle entities such as ReactorCraft neutrons) need rigs at y >= 0; calling methods on a tile whose class mentions an absent optional API (BuildCraft) from Nashorn throwsNoClassDefFoundError, so read fields viaf(). - Gameplay scenarios without a window:
tools/dev/run-server-script.sh <scenario> [autostopTicks] [firstScriptTick] [--fresh]runs a file oftools/dev/server-scripts/scenarioswith the helpers oflib.js(platform, placement through the real item use with a fake player, field access, a task scheduler) andrig.js(engine, shafts, machine in a line, also machines fed from below through a bevel gear). The RotaryCraft set builds real engines, gearboxes, clutches and machines, powers them and asserts power, fluids, items, GUIs (containers and shift-click), capabilities (items, fluids, RF), persistence across a restart (persist, run twice) and the handbook data. Jars go inrun-subtest/mods/1.7.10. - Legacy mod jars are supplied by the developer and never committed (
test-mods/is gitignored).
Like the Sinytra Connector checker, tools/compat/probe.py translates mod jars offline, without starting the game, and records whether every
reference links:
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
python3 tools/compat/probe.py test-mods2 --mc 1.7.10 --version 0.3.18 # --dry-run to preview, --json out.json keeps the raw records
python3 tools/compat/probe.py --from-json out.json --mc 1.7.10 # convert an earlier run
- It runs
./gradlew checkLegacyMod -PlegacyJar=... -PcheckJson=<file>on all jars as ONE mod set (mods that extend each other link), then merges the per-jar records intodocs/data/mods.jsonwith sourceprobe. -
links(every reference resolves; missing legacy API is synthesized) becomes statusloads,unresolvedbecomesfailswith the first unresolved references in the note. Engine patches (obsolete) and library jars without a mod are not listed. - A probe proves only that a jar translates and links, never that it works in game. The site ranks it below player reports, maintainer, client and startup-test results, and the script never overwrites those. Re-running refreshes earlier probe entries.
- Every run is also appended to
docs/data/probe.json(counts per result plus the per-mod list of the last 10 runs per version), which the pie chart pagedocs/probe.html("Automated results" on the compatibility site) shows with a run selector and a history table. -
Jar sources.
tools/compat/fetch_probe_mods.py <mc> <dir> [--limit N]downloads the most downloaded Forge mods of a version from Modrinth's public API (SHA-1 checked). The jars are only for the probe: keep them in a gitignored or temporary folder, never commit or redistribute them. -
CI.
.github/workflows/probe.ymlruns fetch + probe for 1.7.10 and 1.12.2 on every published release, weekly and on demand (workflow_dispatch, inputlimit), then commitsdocs/data/mods.jsonanddocs/data/probe.json. The jars exist only on the runner. Local runs against your own corpus (test-mods2) work the same way, only the committed results matter.