Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
@@ -35,6 +35,7 @@ jobs:
|
||||
- name: Проверка generated assets
|
||||
run: |
|
||||
python3 -m pip install --user -r tools/requirements.txt
|
||||
python3 tools/gen_catalog.py --check
|
||||
python3 tools/gen_models.py --check
|
||||
python3 -m unittest discover -s tools -p test_model_bounds.py
|
||||
- name: Тесты ядра без SDK
|
||||
|
||||
@@ -52,37 +52,8 @@ impl Unit {
|
||||
}
|
||||
|
||||
use DroneKind::*;
|
||||
/// The raid entries come first, in the order stored by existing scenario files.
|
||||
pub const AIRCRAFT: &[Aircraft] = &[
|
||||
Aircraft { kind: Gerbera, launch_item: None, raid: true },
|
||||
Aircraft { kind: Shahed, launch_item: Some("airdef:launch_shahed"), raid: true },
|
||||
Aircraft { kind: Geran3, launch_item: Some("airdef:launch_geran3"), raid: true },
|
||||
Aircraft { kind: Lancet, launch_item: Some("airdef:launch_lancet"), raid: true },
|
||||
Aircraft { kind: Kh101, launch_item: Some("airdef:launch_kh101"), raid: true },
|
||||
Aircraft { kind: Kalibr, launch_item: Some("airdef:launch_kalibr"), raid: true },
|
||||
Aircraft { kind: Kh22, launch_item: Some("airdef:launch_kh22"), raid: true },
|
||||
Aircraft { kind: Zircon, launch_item: Some("airdef:launch_zircon"), raid: true },
|
||||
Aircraft { kind: Iskander, launch_item: Some("airdef:launch_iskander"), raid: true },
|
||||
Aircraft { kind: Kinzhal, launch_item: Some("airdef:launch_kinzhal"), raid: true },
|
||||
Aircraft { kind: Oreshnik, launch_item: Some("airdef:launch_oreshnik"), raid: true },
|
||||
Aircraft { kind: OreshnikBlock, launch_item: None, raid: false },
|
||||
Aircraft { kind: OreshnikSub, launch_item: None, raid: false },
|
||||
];
|
||||
|
||||
macro_rules! units {
|
||||
($( $kind:ident => $item:literal ),+ $(,)?) => {
|
||||
pub const UNITS: &[Unit] = &[$(Unit { kind: SamKind::$kind, deploy_item: $item }),+];
|
||||
};
|
||||
}
|
||||
units! {
|
||||
Patriot => "airdef:deploy_patriot", Gepard => "airdef:deploy_gepard",
|
||||
Pantsir => "airdef:deploy_pantsir", S400 => "airdef:deploy_s400",
|
||||
S500 => "airdef:deploy_s500", IrisT => "airdef:deploy_irist",
|
||||
Nasams => "airdef:deploy_nasams", Buk => "airdef:deploy_buk",
|
||||
Tor => "airdef:deploy_tor", P1sun => "airdef:deploy_p1sun",
|
||||
Radar => "airdef:deploy_radar", Depot => "airdef:deploy_depot",
|
||||
Tzm => "airdef:deploy_tzm", Ew => "airdef:deploy_ew", Light => "airdef:deploy_light",
|
||||
}
|
||||
// Generated entries keep the historical raid and unit ordering.
|
||||
include!("catalog_entries.rs");
|
||||
|
||||
/// Existing positional serialization makes the first entries' order part of the save format.
|
||||
const fn raid_count() -> usize {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Generated by tools/gen_catalog.py from catalog/content.json. Do not edit.
|
||||
pub const AIRCRAFT: &[Aircraft] = &[
|
||||
Aircraft { kind: Gerbera, launch_item: None, raid: true },
|
||||
Aircraft { kind: Shahed, launch_item: Some("airdef:launch_shahed"), raid: true },
|
||||
Aircraft { kind: Geran3, launch_item: Some("airdef:launch_geran3"), raid: true },
|
||||
Aircraft { kind: Lancet, launch_item: Some("airdef:launch_lancet"), raid: true },
|
||||
Aircraft { kind: Kh101, launch_item: Some("airdef:launch_kh101"), raid: true },
|
||||
Aircraft { kind: Kalibr, launch_item: Some("airdef:launch_kalibr"), raid: true },
|
||||
Aircraft { kind: Kh22, launch_item: Some("airdef:launch_kh22"), raid: true },
|
||||
Aircraft { kind: Zircon, launch_item: Some("airdef:launch_zircon"), raid: true },
|
||||
Aircraft { kind: Iskander, launch_item: Some("airdef:launch_iskander"), raid: true },
|
||||
Aircraft { kind: Kinzhal, launch_item: Some("airdef:launch_kinzhal"), raid: true },
|
||||
Aircraft { kind: Oreshnik, launch_item: Some("airdef:launch_oreshnik"), raid: true },
|
||||
Aircraft { kind: OreshnikBlock, launch_item: None, raid: false },
|
||||
Aircraft { kind: OreshnikSub, launch_item: None, raid: false },
|
||||
];
|
||||
|
||||
pub const UNITS: &[Unit] = &[
|
||||
Unit { kind: SamKind::Patriot, deploy_item: "airdef:deploy_patriot" },
|
||||
Unit { kind: SamKind::Gepard, deploy_item: "airdef:deploy_gepard" },
|
||||
Unit { kind: SamKind::Pantsir, deploy_item: "airdef:deploy_pantsir" },
|
||||
Unit { kind: SamKind::S400, deploy_item: "airdef:deploy_s400" },
|
||||
Unit { kind: SamKind::S500, deploy_item: "airdef:deploy_s500" },
|
||||
Unit { kind: SamKind::IrisT, deploy_item: "airdef:deploy_irist" },
|
||||
Unit { kind: SamKind::Nasams, deploy_item: "airdef:deploy_nasams" },
|
||||
Unit { kind: SamKind::Buk, deploy_item: "airdef:deploy_buk" },
|
||||
Unit { kind: SamKind::Tor, deploy_item: "airdef:deploy_tor" },
|
||||
Unit { kind: SamKind::P1sun, deploy_item: "airdef:deploy_p1sun" },
|
||||
Unit { kind: SamKind::Radar, deploy_item: "airdef:deploy_radar" },
|
||||
Unit { kind: SamKind::Depot, deploy_item: "airdef:deploy_depot" },
|
||||
Unit { kind: SamKind::Tzm, deploy_item: "airdef:deploy_tzm" },
|
||||
Unit { kind: SamKind::Ew, deploy_item: "airdef:deploy_ew" },
|
||||
Unit { kind: SamKind::Light, deploy_item: "airdef:deploy_light" },
|
||||
];
|
||||
@@ -0,0 +1,394 @@
|
||||
{
|
||||
"aircraft": [
|
||||
{
|
||||
"kind": "Gerbera",
|
||||
"item": null,
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "Shahed",
|
||||
"item": "launch_shahed",
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "Geran3",
|
||||
"item": "launch_geran3",
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "Lancet",
|
||||
"item": "launch_lancet",
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "Kh101",
|
||||
"item": "launch_kh101",
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "Kalibr",
|
||||
"item": "launch_kalibr",
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "Kh22",
|
||||
"item": "launch_kh22",
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "Zircon",
|
||||
"item": "launch_zircon",
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "Iskander",
|
||||
"item": "launch_iskander",
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "Kinzhal",
|
||||
"item": "launch_kinzhal",
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "Oreshnik",
|
||||
"item": "launch_oreshnik",
|
||||
"raid": true
|
||||
},
|
||||
{
|
||||
"kind": "OreshnikBlock",
|
||||
"item": null,
|
||||
"raid": false
|
||||
},
|
||||
{
|
||||
"kind": "OreshnikSub",
|
||||
"item": null,
|
||||
"raid": false
|
||||
}
|
||||
],
|
||||
"units": [
|
||||
{
|
||||
"kind": "Patriot",
|
||||
"item": "deploy_patriot"
|
||||
},
|
||||
{
|
||||
"kind": "Gepard",
|
||||
"item": "deploy_gepard"
|
||||
},
|
||||
{
|
||||
"kind": "Pantsir",
|
||||
"item": "deploy_pantsir"
|
||||
},
|
||||
{
|
||||
"kind": "S400",
|
||||
"item": "deploy_s400"
|
||||
},
|
||||
{
|
||||
"kind": "S500",
|
||||
"item": "deploy_s500"
|
||||
},
|
||||
{
|
||||
"kind": "IrisT",
|
||||
"item": "deploy_irist"
|
||||
},
|
||||
{
|
||||
"kind": "Nasams",
|
||||
"item": "deploy_nasams"
|
||||
},
|
||||
{
|
||||
"kind": "Buk",
|
||||
"item": "deploy_buk"
|
||||
},
|
||||
{
|
||||
"kind": "Tor",
|
||||
"item": "deploy_tor"
|
||||
},
|
||||
{
|
||||
"kind": "P1sun",
|
||||
"item": "deploy_p1sun"
|
||||
},
|
||||
{
|
||||
"kind": "Radar",
|
||||
"item": "deploy_radar"
|
||||
},
|
||||
{
|
||||
"kind": "Depot",
|
||||
"item": "deploy_depot"
|
||||
},
|
||||
{
|
||||
"kind": "Tzm",
|
||||
"item": "deploy_tzm"
|
||||
},
|
||||
{
|
||||
"kind": "Ew",
|
||||
"item": "deploy_ew"
|
||||
},
|
||||
{
|
||||
"kind": "Light",
|
||||
"item": "deploy_light"
|
||||
}
|
||||
],
|
||||
"items": [
|
||||
{
|
||||
"id": "designator",
|
||||
"title": "Целеуказатель",
|
||||
"color": [
|
||||
220,
|
||||
50,
|
||||
50
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "launch_shahed",
|
||||
"title": "Пуск: Шахед-136",
|
||||
"color": [
|
||||
90,
|
||||
92,
|
||||
96
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "launch_geran3",
|
||||
"title": "Пуск: Герань-3",
|
||||
"color": [
|
||||
30,
|
||||
30,
|
||||
34
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_patriot",
|
||||
"title": "Развернуть Patriot",
|
||||
"color": [
|
||||
76,
|
||||
86,
|
||||
58
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_gepard",
|
||||
"title": "Развернуть Гепард",
|
||||
"color": [
|
||||
104,
|
||||
84,
|
||||
54
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_pantsir",
|
||||
"title": "Развернуть Панцирь-С1",
|
||||
"color": [
|
||||
82,
|
||||
96,
|
||||
60
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_s400",
|
||||
"title": "Развернуть С-400",
|
||||
"color": [
|
||||
60,
|
||||
80,
|
||||
50
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_s500",
|
||||
"title": "Развернуть С-500",
|
||||
"color": [
|
||||
108,
|
||||
110,
|
||||
80
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "launch_lancet",
|
||||
"title": "Пуск: Ланцет-3",
|
||||
"color": [
|
||||
70,
|
||||
78,
|
||||
66
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "launch_kh101",
|
||||
"title": "Пуск: Х-101",
|
||||
"color": [
|
||||
84,
|
||||
88,
|
||||
92
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "launch_kalibr",
|
||||
"title": "Пуск: Калибр",
|
||||
"color": [
|
||||
150,
|
||||
154,
|
||||
156
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "launch_kh22",
|
||||
"title": "Пуск: Х-22",
|
||||
"color": [
|
||||
226,
|
||||
228,
|
||||
222
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "launch_zircon",
|
||||
"title": "Пуск: Циркон",
|
||||
"color": [
|
||||
52,
|
||||
54,
|
||||
52
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "launch_iskander",
|
||||
"title": "Пуск: Искандер-М",
|
||||
"color": [
|
||||
108,
|
||||
110,
|
||||
80
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "launch_kinzhal",
|
||||
"title": "Пуск: Кинжал",
|
||||
"color": [
|
||||
60,
|
||||
62,
|
||||
60
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "launch_oreshnik",
|
||||
"title": "Пуск: Орешник",
|
||||
"color": [
|
||||
230,
|
||||
120,
|
||||
40
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_irist",
|
||||
"title": "Развернуть IRIS-T SLM",
|
||||
"color": [
|
||||
76,
|
||||
86,
|
||||
58
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_nasams",
|
||||
"title": "Развернуть NASAMS",
|
||||
"color": [
|
||||
76,
|
||||
86,
|
||||
58
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_buk",
|
||||
"title": "Развернуть Бук-М2",
|
||||
"color": [
|
||||
82,
|
||||
96,
|
||||
60
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_tor",
|
||||
"title": "Развернуть Тор-М2",
|
||||
"color": [
|
||||
108,
|
||||
110,
|
||||
80
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_p1sun",
|
||||
"title": "Развернуть P1-SUN",
|
||||
"color": [
|
||||
52,
|
||||
54,
|
||||
52
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_radar",
|
||||
"title": "Развернуть РЛС дальнего обнаружения",
|
||||
"color": [
|
||||
108,
|
||||
110,
|
||||
80
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_depot",
|
||||
"title": "Развернуть склад боеприпасов",
|
||||
"color": [
|
||||
92,
|
||||
78,
|
||||
48
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_tzm",
|
||||
"title": "Развернуть ТЗМ",
|
||||
"color": [
|
||||
82,
|
||||
96,
|
||||
60
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_ew",
|
||||
"title": "Развернуть станцию РЭБ",
|
||||
"color": [
|
||||
108,
|
||||
110,
|
||||
80
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy_light",
|
||||
"title": "Развернуть прожектор",
|
||||
"color": [
|
||||
52,
|
||||
54,
|
||||
52
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mover",
|
||||
"title": "Пульт перемещения",
|
||||
"color": [
|
||||
60,
|
||||
140,
|
||||
220
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "flare_gun",
|
||||
"title": "Осветительная ракета",
|
||||
"color": [
|
||||
255,
|
||||
240,
|
||||
180
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "remover",
|
||||
"title": "Свернуть ПВО",
|
||||
"color": [
|
||||
200,
|
||||
200,
|
||||
200
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+2
-1
@@ -34,6 +34,7 @@
|
||||
Команды:
|
||||
|
||||
- `python3 -m pip install -r tools/requirements.txt` — зависимости генератора (закреплён Pillow).
|
||||
- `python3 tools/gen_catalog.py` — обновить Rust-константы после изменений `catalog/content.json`; `--check` проверяет актуальность.
|
||||
- `python3 tools/gen_models.py` — перегенерировать.
|
||||
- `python3 tools/gen_models.py --check` — только сравнить, выход 1 при различиях.
|
||||
|
||||
@@ -52,7 +53,7 @@
|
||||
- `tools/airdef_assets/bounds.py` экспортирует габариты итоговых моделей для headless-навигации; authored overrides входят в расчёт.
|
||||
- `tools/gen_tex.py` отвечает за вывод процедурных текстур и иконок; переиспользуемые примитивы палитры также находятся здесь.
|
||||
- `tools/airdef_assets/texture_catalog.py` связывает стабильную палитру и процедурные функции с текстурами моделей, не смешивая каталог с геометрией.
|
||||
- `tools/airdef_assets/icons.py` содержит каталог предметов и чистую функцию рендера иконки с явными параметрами `(name, color, seed)`.
|
||||
- `catalog/content.json` задаёт идентичности техники и предметов и оформление предметов; `tools/airdef_assets/icons.py` читает его и содержит чистую функцию рендера иконки с явными параметрами `(name, color, seed)`.
|
||||
- `tools/airdef_assets/pipeline.py` проверяет manifest, рассчитывает безопасный план обновления и откатывает записи при исключении.
|
||||
- `airdef-mod/source-assets/assets/airdef/` — источник авторских переопределений. Генератор объединяет его с процедурными ресурсами во временном дереве, проверяет ссылки/JSON/PNG, затем устанавливает runtime-файлы.
|
||||
- `airdef-mod/assets/` и `airdef-mod/data/` — упакованные generated output. Не редактируйте их вручную; добавляйте авторские исходники в `source-assets`.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
"""Shared content identities and item presentation used by runtime and assets."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
PATH = Path(__file__).resolve().parents[2] / "catalog" / "content.json"
|
||||
|
||||
|
||||
def load():
|
||||
data = json.loads(PATH.read_text(encoding="utf-8"))
|
||||
items = data["items"]
|
||||
ids = [item["id"] for item in items]
|
||||
if len(ids) != len(set(ids)):
|
||||
raise ValueError("duplicate item ID in content catalog")
|
||||
referenced = []
|
||||
for group in (data["aircraft"], data["units"]):
|
||||
kinds = [entry["kind"] for entry in group]
|
||||
if len(kinds) != len(set(kinds)):
|
||||
raise ValueError("duplicate kind in content catalog")
|
||||
referenced.extend(entry["item"] for entry in group if entry["item"] is not None)
|
||||
if len(referenced) != len(set(referenced)) or not set(referenced) <= set(ids):
|
||||
raise ValueError("duplicate or unregistered content item")
|
||||
for item in items:
|
||||
if len(item["color"]) != 3 or any(not isinstance(c, int) or not 0 <= c <= 255 for c in item["color"]):
|
||||
raise ValueError(f"invalid icon color for {item['id']}")
|
||||
return data
|
||||
@@ -6,38 +6,10 @@ from PIL import Image
|
||||
|
||||
from .texture_catalog import clamp
|
||||
|
||||
# Предметы: (id, название, цвет иконки)
|
||||
ITEMS = [
|
||||
("designator", "Целеуказатель", (220, 50, 50)),
|
||||
("launch_shahed", "Пуск: Шахед-136", (90, 92, 96)),
|
||||
("launch_geran3", "Пуск: Герань-3", (30, 30, 34)),
|
||||
("deploy_patriot", "Развернуть Patriot", (76, 86, 58)),
|
||||
("deploy_gepard", "Развернуть Гепард", (104, 84, 54)),
|
||||
("deploy_pantsir", "Развернуть Панцирь-С1", (82, 96, 60)),
|
||||
("deploy_s400", "Развернуть С-400", (60, 80, 50)),
|
||||
("deploy_s500", "Развернуть С-500", (108, 110, 80)),
|
||||
("launch_lancet", "Пуск: Ланцет-3", (70, 78, 66)),
|
||||
("launch_kh101", "Пуск: Х-101", (84, 88, 92)),
|
||||
("launch_kalibr", "Пуск: Калибр", (150, 154, 156)),
|
||||
("launch_kh22", "Пуск: Х-22", (226, 228, 222)),
|
||||
("launch_zircon", "Пуск: Циркон", (52, 54, 52)),
|
||||
("launch_iskander", "Пуск: Искандер-М", (108, 110, 80)),
|
||||
("launch_kinzhal", "Пуск: Кинжал", (60, 62, 60)),
|
||||
("launch_oreshnik", "Пуск: Орешник", (230, 120, 40)),
|
||||
("deploy_irist", "Развернуть IRIS-T SLM", (76, 86, 58)),
|
||||
("deploy_nasams", "Развернуть NASAMS", (76, 86, 58)),
|
||||
("deploy_buk", "Развернуть Бук-М2", (82, 96, 60)),
|
||||
("deploy_tor", "Развернуть Тор-М2", (108, 110, 80)),
|
||||
("deploy_p1sun", "Развернуть P1-SUN", (52, 54, 52)),
|
||||
("deploy_radar", "Развернуть РЛС дальнего обнаружения", (108, 110, 80)),
|
||||
("deploy_depot", "Развернуть склад боеприпасов", (92, 78, 48)),
|
||||
("deploy_tzm", "Развернуть ТЗМ", (82, 96, 60)),
|
||||
("deploy_ew", "Развернуть станцию РЭБ", (108, 110, 80)),
|
||||
("deploy_light", "Развернуть прожектор", (52, 54, 52)),
|
||||
("mover", "Пульт перемещения", (60, 140, 220)),
|
||||
("flare_gun", "Осветительная ракета", (255, 240, 180)),
|
||||
("remover", "Свернуть ПВО", (200, 200, 200)),
|
||||
]
|
||||
# Presentation follows the shared content catalog; order keeps generated assets stable.
|
||||
from .catalog import load as load_catalog
|
||||
|
||||
ITEMS = [(item["id"], item["title"], tuple(item["color"])) for item in load_catalog()["items"]]
|
||||
|
||||
|
||||
# Иконки 16×16 без сглаживания. Каждый предмет рисуется своей функцией: категория задаёт основу
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate Rust content entries from the shared catalog; --check detects drift."""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
from airdef_assets.catalog import load
|
||||
|
||||
OUTPUT = Path(__file__).resolve().parents[1] / "airdef-mod/src/sim/catalog_entries.rs"
|
||||
|
||||
|
||||
def generate(data):
|
||||
lines = ["// Generated by tools/gen_catalog.py from catalog/content.json. Do not edit.",
|
||||
"pub const AIRCRAFT: &[Aircraft] = &["]
|
||||
for entry in data["aircraft"]:
|
||||
kind, item = entry["kind"], entry["item"]
|
||||
if not re.fullmatch(r"[A-Z][A-Za-z0-9]*", kind):
|
||||
raise ValueError(f"invalid Rust kind {kind!r}")
|
||||
if item is not None and not re.fullmatch(r"[a-z0-9_]+", item):
|
||||
raise ValueError(f"invalid item ID {item!r}")
|
||||
item_expr = f'Some("airdef:{item}")' if item is not None else "None"
|
||||
lines.append(f' Aircraft {{ kind: {kind}, launch_item: {item_expr}, raid: {str(entry["raid"]).lower()} }},')
|
||||
lines += ["];", "", "pub const UNITS: &[Unit] = &["]
|
||||
for entry in data["units"]:
|
||||
kind, item = entry["kind"], entry["item"]
|
||||
if not re.fullmatch(r"[A-Z][A-Za-z0-9]*", kind) or not re.fullmatch(r"[a-z0-9_]+", item):
|
||||
raise ValueError(f"invalid unit {entry!r}")
|
||||
lines.append(f' Unit {{ kind: SamKind::{kind}, deploy_item: "airdef:{item}" }},')
|
||||
return "\n".join(lines + ["];", ""])
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--check", action="store_true")
|
||||
args = parser.parse_args()
|
||||
generated = generate(load())
|
||||
if args.check:
|
||||
if not OUTPUT.exists() or OUTPUT.read_text(encoding="utf-8") != generated:
|
||||
parser.exit(1, f"{OUTPUT} is stale; run python3 tools/gen_catalog.py\n")
|
||||
else:
|
||||
OUTPUT.write_text(generated, encoding="utf-8")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -22,6 +24,16 @@ gen_models = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(gen_models)
|
||||
|
||||
|
||||
class ContentCatalogTests(unittest.TestCase):
|
||||
def test_catalog_rust_entries_are_current_and_registered_items_match_assets(self):
|
||||
root = TOOLS.parent
|
||||
subprocess.run([sys.executable, str(TOOLS / "gen_catalog.py"), "--check"], check=True)
|
||||
language = json.loads((root / "airdef-mod/assets/airdef/lang/ru_ru.json").read_text(encoding="utf-8"))
|
||||
for name, title, _ in icons.ITEMS:
|
||||
self.assertEqual(language[f"item.airdef.{name}"], title)
|
||||
self.assertTrue((root / "airdef-mod/data/airdef/horizon/item" / (name + ".json")).is_file())
|
||||
|
||||
|
||||
class ManifestTests(unittest.TestCase):
|
||||
def test_manifest_rejects_traversal_and_duplicate_paths(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
|
||||
Reference in New Issue
Block a user