README приведён в соответствие с тем, что реально сделано
This commit is contained in:
@@ -37,14 +37,22 @@ vanilla client-1.12.2.jar verified Mojang signature
|
||||
-> merge with MCP CSVs 9991 fields, 18715 methods renamed
|
||||
-> SpecialSource 3310 classes in MCP names
|
||||
-> FernFlower 2050 source files
|
||||
-> hand patches + rules 103 patches, one regex rule
|
||||
-> javac 3261 classes, clean
|
||||
-> hand patches + rules 114 patches, one regex rule
|
||||
-> javac 3268 classes, clean
|
||||
```
|
||||
|
||||
`prepare-game.ps1` runs the whole thing and is idempotent. Hand fixes live in
|
||||
`game-fork\patches\decompiler-fixes.txt` and are re-applied on every run, so
|
||||
regenerating the tree never loses them.
|
||||
|
||||
Compiling clean proves nothing. Decompilation quietly breaks code that still
|
||||
compiles, still has the right methods and still runs: an entity that compares
|
||||
itself with itself never leaves the ground, a list indexed by entity id
|
||||
removes the wrong player. Every patch here was checked against the original
|
||||
bytecode with `javap`, and `tools\javap_diff.py` is the tool that makes that
|
||||
comparison usable. The self-comparison finder and the remove-by-index finder
|
||||
both came out of this class of bug.
|
||||
|
||||
The fork holds Mojang-derived code and is **not published**. What ships in the
|
||||
open repository is the loader, the mod API and the build tools.
|
||||
|
||||
@@ -53,12 +61,12 @@ open repository is the loader, the mod API and the build tools.
|
||||
Mods are jars with a descriptor at `META-INF/rsxmod.properties`:
|
||||
|
||||
```properties
|
||||
id=rsxm-lib
|
||||
name=RustNotMC Library
|
||||
version=1.0.0
|
||||
id=rsxm-demo
|
||||
name=RustNotMC Demo
|
||||
version=1.1.0
|
||||
env=CLIENT
|
||||
entrypoint=net.rustnotmc.lib.LibMod
|
||||
access=hud, text, world, player
|
||||
entrypoint=net.rustnotmc.demo.DemoMod
|
||||
access=hud,commands,files
|
||||
depends.core=>=1.0
|
||||
```
|
||||
|
||||
@@ -66,34 +74,84 @@ depends.core=>=1.0
|
||||
the field exists so a mod that targets both sides does not need a format
|
||||
change when the loader moves to the server.
|
||||
|
||||
Each mod gets its own class loader. Mods declare the capabilities they need and
|
||||
the loader grants them; `reflection` is never granted without explicit operator
|
||||
consent.
|
||||
Each mod gets its own class loader. Load order comes from the dependency graph,
|
||||
not from filenames. Shutdown runs in reverse.
|
||||
|
||||
Load order comes from the dependency graph, not from filenames. Shutdown runs
|
||||
in reverse.
|
||||
### What a mod may touch
|
||||
|
||||
A client mod changes what the client shows. It never decides what is true about
|
||||
the world: the server owns the world, the inventory, the player and the
|
||||
network. Capabilities are only granted when something actually checks them.
|
||||
|
||||
| Capability | Grants | Checked |
|
||||
|---|---|---|
|
||||
| `CORE` | lifecycle, logging, own mod directory | always |
|
||||
| `HUD` | draw overlays through the game's font | granted from descriptor |
|
||||
| `INPUT` | read keyboard and mouse state | granted from descriptor |
|
||||
| `COMMANDS` | register commands the player can type | granted from descriptor |
|
||||
| `FILES` | a directory of its own under `run/mods/<id>` | granted from descriptor |
|
||||
| `REFLECTION` | reach into game internals | never automatic, operator consent |
|
||||
|
||||
`WORLD`, `PLAYER`, `NETWORK`, `TEXT`, `INPUT` and `COMMAND` were removed: they
|
||||
were advertised but not enforced, so a descriptor could claim them and receive
|
||||
nothing. A capability that cannot be granted is worse than an absent one.
|
||||
|
||||
Commands carry a scope. `CLIENT` commands run on the client and are never sent
|
||||
anywhere. `SERVER` and `BOTH` commands are registered but deliberately not
|
||||
executed on the client, because the server is what has to see them and decide.
|
||||
|
||||
The client API contains no game types at all, which is what lets the loader
|
||||
build and test with the game closed: the SPI is implemented on the game side
|
||||
and the API never depends on it. `tick()` advances on the client's own tick,
|
||||
not on frames, so a mod counts the same twenty steps a second at 60 fps and at
|
||||
240.
|
||||
|
||||
## Building
|
||||
|
||||
```powershell
|
||||
rsx-client\scripts\build.ps1 # dist\rsx-loader.jar
|
||||
rsx-client\scripts\test.ps1 # self test, 96 checks
|
||||
game-fork\scripts\prepare-game.ps1 # rebuild the fork from the vanilla jar
|
||||
game-fork\scripts\compile-game.ps1 # compile only, fast iteration
|
||||
rsx-client\scripts\build.ps1 # dist\rsx-loader.jar
|
||||
rsx-client\scripts\test.ps1 # self test, 128 checks
|
||||
game-fork\scripts\prepare-game.ps1 # rebuild the fork from the vanilla jar
|
||||
game-fork\scripts\compile-game.ps1 # compile only, fast iteration
|
||||
rsx-client\scripts\run-game.ps1 # launch the client
|
||||
```
|
||||
|
||||
Only a JDK is required. No Gradle, no network once the mappings are cached.
|
||||
|
||||
### Assets
|
||||
|
||||
The client will not run without a real asset tree, and the launcher on this
|
||||
machine keeps assets the modern way: `indexes/1.12.json` pointing at
|
||||
`objects/<aa>/<sha1>`. A 1.12.2 client reads neither. It wants
|
||||
`<assetsDir>/minecraft/<path>`.
|
||||
|
||||
Pointing `--assetsDir` straight at the object store does not look broken. Text
|
||||
and textures come from the jar, so the game looks complete, but every sound has
|
||||
been an asset since 1.7.3 and none of them are on disk: the sound engine
|
||||
starts, `play()` is called, nothing is found, and paulscode says nothing.
|
||||
`tools\materialize_assets.py` lays the tree out and `run-game.ps1` calls it on
|
||||
every launch, so this cannot be forgotten again.
|
||||
|
||||
## Tools
|
||||
|
||||
| Script | Purpose |
|
||||
|---|---|
|
||||
| `rsx-client\tools\merge_srg_mcp.py` | vanilla SRG plus MCP CSVs into one obf to MCP SRG |
|
||||
| `rsx-client\tools\apply_patches.py` | re-applies the hand fixes, fails loudly if one misses |
|
||||
| `rsx-client\tools\add_patch.py` | appends a reviewed patch to the fix file |
|
||||
| `rsx-client\tools\fix_stale_locals.py` | FernFlower's stale local references, scoped per method |
|
||||
| `rsx-client\tools\fix_generic_casts.py` | inserts the cast where a generic loses its type parameter |
|
||||
| `rsx-client\tools\fix_decompiler_rules.py` | systematic rewrites that would be hundreds of patches |
|
||||
| `rsx-client\tools\show_errors.py` | javac errors next to the source line that caused them |
|
||||
| `rsx-client\tools\struct_diff.py` | class-by-class comparison against the reference, plus orphan analysis |
|
||||
| `rsx-client\tools\javap_diff.py` | instruction-level comparison, normalising constant pool and offsets |
|
||||
| `rsx-client\tools\find_self_comparisons.py` | finds `x == x`, the decompiler's signature bug |
|
||||
| `rsx-client\tools\find_remove_index_bugs.py` | finds `List<Integer>.remove(int)` where an object was meant |
|
||||
| `rsx-client\tools\find_callers.py` | who calls a field or method, via the constant pool |
|
||||
| `rsx-client\tools\materialize_assets.py` | lays the asset tree out from the index and object store |
|
||||
| `rsx-client\tools\ogg_duration.py` | real length of an ogg, to tell a short file from a cut-off one |
|
||||
| `rsx-client\tools\stage_run.py` | writes the runtime classpath |
|
||||
| `rsx-client\tools\fetch_missing.py` | pulls natives into the local run directory |
|
||||
|
||||
## Legal
|
||||
|
||||
|
||||
Reference in New Issue
Block a user