Files
horizon/tools/generate_command_order.py
Ludovic MarieandClaude Opus 5.5 0a116d9523 Split horizon-command and horizon-client-core out of horizon-client
A pure move, the first slice of splitting the client lib into crates:

- horizon-command: the command/ module (Brigadier port, vanilla tree, argument types, selectors), as is.
- horizon-client-core: what command needs from the lib, with no other dependency inside it: text, lang, snbt,
  nbt_path, component, scoreboard, bossbar, gamerules, block_entity_nbt, fastutil_order; plus items moved out of
  lib modules that sit in the cycle, unchanged: ParticleType (particles.rs), the key display name (keybinds.rs),
  SOUND_SOURCES (settings.rs), uuid_text/uuid_from_text (save.rs), Registries/RegistryIds and parse_identifier
  (command/args.rs).

horizon-client re-exports every module and item under its old path (`horizon_client::text`,
`crate::command::Registries`, `particles::ParticleType`...), so no caller changes. Inside the moved files only
paths changed (`crate::snbt` -> `horizon_client_core::snbt` in command, `crate::command::args::parse_identifier`
-> `crate::identifier::parse_identifier`...), `sign_side` became pub, lang.rs includes the lang files from
horizon-client's assets, and gamerules' test that saves a Level moved to save.rs (it needs the client's save).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-26 13:36:05 +01:00

63 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""Generate crates/horizon-command/src/vanilla_order.rs: the order of
the children of each node of vanilla's command tree where Horizon's tree
registers them in a different order. Brigadier keeps children in
registration order, and /help (smart usage) lists them that way.
python3 tools/test_command_parse_parity.py # exports .local/command-parse-parity/tree.json
command_parse_fixture tree rust_tree.json # Horizon's tree in its registration order
generate_command_order.py VANILLA_TREE RUST_TREE
"""
import argparse
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def main():
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("vanilla", type=Path)
parser.add_argument("rust", type=Path)
parser.add_argument("--out", type=Path, default=ROOT / "crates/horizon-command/src/vanilla_order.rs")
args = parser.parse_args()
vanilla = json.loads(args.vanilla.read_text())
rust = json.loads(args.rust.read_text())
rows = []
def walk(v, r, path):
names = list(v.get("children", {}))
# GameRuleCommand registers the rules in a Reference2ObjectOpenHashMap's
# (identity hash) order, which changes from run to run.
if path == ["gamerule"]:
return
order = r.get("order", [])
shared = [n for n in names if n in order]
if shared != [n for n in order if n in names]:
rows.append((" ".join(path), shared))
for n in names:
if n in r.get("children", {}):
walk(v["children"][n], r["children"][n], path + [n])
walk(vanilla, rust, [])
if not rows or rows[0][0] != "":
raise SystemExit("expected the root's order to differ")
lines = [
"//! The order of node children in vanilla's command tree, for the nodes",
"//! where Horizon's tree registers them differently (path of node names",
"//! from the root, children in vanilla's order). Generated by",
"//! tools/generate_command_order.py; do not edit by hand.",
"",
"pub const ORDER: &[(&str, &[&str])] = &[",
]
for path, names in rows:
joined = ", ".join(f'"{n}"' for n in names)
lines.append(f' ("{path}", &[{joined}]),')
lines += ["];", ""]
args.out.write_text("\n".join(lines))
print(f"wrote {len(rows)} nodes to {args.out}")
if __name__ == "__main__":
main()