mirror of
https://github.com/horizon-rust/horizon-mods.git
synced 2026-11-07 02:56:08 -10:00
native-compat: remove [api] and [dependencies] before horizon-mixins, as the launcher does (#364)
The launcher strips these launcher-only tables before horizon-mixins reads a bundle's manifest (native_mods/api.rs). The check passed them through, so a mod with a trait package (cpm, #317) failed with E_MANIFEST "unknown field api" while players' launchers apply it. Mods with an API package are applied but not compiled, as health already leaves them out. Co-authored-by: Horizon agent <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2 files changed
+77
-1
No files matched your search
@@ -79,7 +79,27 @@ class Bundle:
|
||||
# As the launcher does for a mod built on another Horizon than it pins.
|
||||
toml = out / "horizon-mixin.toml"
|
||||
text = re.sub(r'horizon_revision\s*=\s*"[0-9a-f]{40}"', f'horizon_revision = "{revision}"', toml.read_text())
|
||||
toml.write_text(text)
|
||||
toml.write_text(strip_launcher_tables(text))
|
||||
|
||||
|
||||
# The tables only the launcher reads; it removes them before horizon-mixins sees the manifest (native_mods/api.rs).
|
||||
LAUNCHER_TABLES = ("api", "dependencies")
|
||||
|
||||
|
||||
def strip_launcher_tables(text):
|
||||
"""`text` without its [api] and [dependencies] tables (and their sub-tables), the rest unchanged."""
|
||||
kept, skipping = [], False
|
||||
for line in text.splitlines(keepends=True):
|
||||
header = re.match(r'\s*\[\[?\s*([^\]]+?)\s*\]\]?\s*(#.*)?$', line)
|
||||
if header:
|
||||
skipping = header.group(1).split(".")[0].strip().strip('"\'') in LAUNCHER_TABLES
|
||||
if not skipping:
|
||||
kept.append(line)
|
||||
stripped = "".join(kept)
|
||||
want = {k: v for k, v in tomllib.loads(text).items() if k not in LAUNCHER_TABLES}
|
||||
if tomllib.loads(stripped) != want:
|
||||
sys.exit("horizon-mixin.toml: its [api] or [dependencies] table couldn't be removed line by line")
|
||||
return stripped
|
||||
|
||||
|
||||
def download(url, sha256):
|
||||
@@ -321,6 +341,10 @@ def check(args):
|
||||
failed.append(mod.id)
|
||||
if args.no_compile:
|
||||
continue
|
||||
if mod.api:
|
||||
# Its API package is staged by the launcher only (Bundle.api), as `health` leaves these out too.
|
||||
print("- applies; it has a mod API package, which only the launcher builds, so it isn't compiled here.")
|
||||
continue
|
||||
if compiles(stage):
|
||||
print(f"- applies and compiles with {len(popular) - len(refused)} popular mods.")
|
||||
continue
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"""python3 -m unittest discover -s scripts -p 'test_*.py'"""
|
||||
import tomllib
|
||||
import unittest
|
||||
|
||||
from native_compat import strip_launcher_tables
|
||||
|
||||
MANIFEST = """[mod]
|
||||
id = "example"
|
||||
version = "1.0.0"
|
||||
|
||||
[compatibility]
|
||||
horizon_revision = "0123456789abcdef0123456789abcdef01234567"
|
||||
|
||||
# A trait-mod package.
|
||||
[api]
|
||||
path = "api"
|
||||
|
||||
[dependencies]
|
||||
jei = { optional = true }
|
||||
|
||||
[dependencies.ae2]
|
||||
optional = false
|
||||
|
||||
[[module]]
|
||||
id = "runtime"
|
||||
file = "crates/horizon-client/src/game.rs"
|
||||
name = "example_mod"
|
||||
source = "runtime.rs"
|
||||
|
||||
[[injection]]
|
||||
id = "frame"
|
||||
target = "horizon_client::game::native_hooks::Game<'_>::native_frame"
|
||||
helper = "frame.rs"
|
||||
"""
|
||||
|
||||
|
||||
class StripLauncherTables(unittest.TestCase):
|
||||
def test_removes_only_the_launcher_tables(self):
|
||||
stripped = tomllib.loads(strip_launcher_tables(MANIFEST))
|
||||
original = tomllib.loads(MANIFEST)
|
||||
self.assertNotIn("api", stripped)
|
||||
self.assertNotIn("dependencies", stripped)
|
||||
for key in ("mod", "compatibility", "module", "injection"):
|
||||
self.assertEqual(stripped[key], original[key])
|
||||
|
||||
def test_a_manifest_without_them_is_unchanged(self):
|
||||
plain = MANIFEST.split("# A trait-mod package.")[0] + MANIFEST.split('optional = false\n')[1]
|
||||
self.assertEqual(strip_launcher_tables(plain), plain)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user