diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 87b73e2..d58faaf 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -32,6 +32,8 @@ jobs: run: nice -n 19 python3 tools/airdef.py fork - name: Проверка закреплённого checkout run: python3 -m unittest discover -s tools -p test_engine_checkout.py + - name: Проверка generated assets + run: python3 tools/gen_models.py --check - name: Тесты ядра без SDK working-directory: airdef-core run: | diff --git a/.gitea/workflows/wasm-contract.yml b/.gitea/workflows/wasm-contract.yml new file mode 100644 index 0000000..7d233cf --- /dev/null +++ b/.gitea/workflows/wasm-contract.yml @@ -0,0 +1,20 @@ +# Heavy sandbox runtime is opt-in on the shared 2-core runner. +name: wasm-contract +on: + workflow_dispatch: +env: + CARGO_BUILD_JOBS: "1" + CARGO_INCREMENTAL: "0" +jobs: + contract: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Rust + run: | + curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --target wasm32-unknown-unknown + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + - name: Actual WASM contract + run: nice -n 19 python3 tools/airdef.py test-wasm diff --git a/airdef-mod/tests/wasm_contract.toml b/airdef-mod/tests/wasm_contract.toml new file mode 100644 index 0000000..4f894d7 --- /dev/null +++ b/airdef-mod/tests/wasm_contract.toml @@ -0,0 +1,46 @@ +# Actual .hmod in the engine sandbox, with default fuel/memory limits. +# Missing render replies exercise fallback paths, not GPU correctness. +[[step]] +ticks = 2 +frames = 2 +expect.commands = ["airdef"] + +[[step]] +command = "airdef deploy patriot" + +[[step]] +command = "airdef deploy s400" + +[[step]] +command = "airdef wave mix 40 1500" + +[[step]] +ticks = 240 +frames = 120 + +[[step]] +command = "airdef clear" + +[[step]] +ticks = 140 +frames = 10 + +[[step]] +command = "airdef replay stop" + +[[step]] +title-screen = true +ticks = 2 +frames = 2 + +[[step]] +player = { x = 100.5, y = 64.0, z = 100.5 } +clock = { game-time = 500, day-time = 6000 } +ticks = 3 +frames = 3 + +[[step]] +reload = true +ticks = 2 +frames = 2 +expect.commands = ["airdef"] diff --git a/docs/ENGINE.md b/docs/ENGINE.md index 87a9233..f39c815 100644 --- a/docs/ENGINE.md +++ b/docs/ENGINE.md @@ -38,3 +38,16 @@ python tools/airdef.py play Рядом с airdef.hmod записывается build-identity.json: SHA AirDef, pin/identity движка, SHA256 WIT и артефакта. API-диагностика остаётся в `python tools/airdef.py check`. Нативные тесты не заменяют отдельную игровую проверку графики, звука и экспорта. + +## Проверка настоящего WASM + +`python tools/airdef.py test-wasm` собирает `.hmod` и запускает `tests/*.toml` в настоящем +runtime закреплённого движка. Используется временное storage; игровые миры не нужны. +Сценарий проверяет загрузку/регистрацию команды, два комплекса, налёт, tick/frame, +выход из мира и перезагрузку мода. Действуют лимиты runtime: 64 MiB linear memory, +300 млн fuel на tick, 10 млн на frame, 1 млрд на event, 2 млрд на init. +Это проверка ABI и fallback при отсутствии render replies; она не измеряет GPU, +не подтверждает акустику/MP4 и не заменяет максимальный боевой полигон. + +В CI проверка assets входит в лёгкий build. Wasmtime tier вынесен в ручной workflow +`wasm-contract`, чтобы не компилировать тяжёлый runtime на каждом push слабого общего runner. diff --git a/tools/airdef.py b/tools/airdef.py index 6c8d42f..57fcd12 100644 --- a/tools/airdef.py +++ b/tools/airdef.py @@ -8,6 +8,7 @@ python3 tools/airdef.py play [аргументы игры] # запустить форк на папке игры python3 tools/airdef.py all # всё по порядку: fork, game, mod, install python3 tools/airdef.py check # совпадают ли форк, упаковщик, уровень API мода и ресурсы + python3 tools/airdef.py test-wasm # собрать и прогнать настоящий .hmod в песочнице без GPU Переменные окружения: HORIZON_HOME (папка игры), HORIZON_RESOURCES (подготовленные ресурсы Minecraft 26.3), AIRDEF_GAME (готовый horizon-client вместо собранного). @@ -138,6 +139,17 @@ def hmod() -> Path: sys.exit("Нет airdef.hmod: python3 tools/airdef.py mod") +def test_wasm(): + """The real component and host ABI, with the engine's normal fuel/memory limits.""" + mod() + build_env = dict(os.environ) + build_env["PATH"] = str(Path(cargo()).parent) + os.pathsep + build_env.get("PATH", "") + # `full` is the packer's default feature; the lightweight release packer has no runtime. + run([cargo(), "run", "--locked", "--manifest-path", FORK / "Cargo.toml", + "-p", "cargo-horizon-mod", "--", "horizon-mod", "test", "--hmod", hmod()], + cwd=ROOT / "airdef-mod", env=build_env) + + def install(): home = game_home() mods = home / "horizon-mods" @@ -281,7 +293,7 @@ def main(): if len(sys.argv) < 2: sys.exit(__doc__) cmd, rest = sys.argv[1], sys.argv[2:] - steps = {"fork": fork, "game": game, "mod": mod, "install": install, "check": check} + steps = {"fork": fork, "game": game, "mod": mod, "install": install, "check": check, "test-wasm": test_wasm} if cmd == "play": play(rest) elif cmd == "all":