The Windows log for #122 showed the mod stopping in the UI event (fuel budget 1e9, remaining 0) with known_units <- order_raid <- launch_wave <- ui_select: order_raid recomputed the planner snapshot known_units() for every drone, and the snapshot itself rescanned the whole force and the dedup list per intel entry. A second big Smart order cost drones x intel-log x force-size inside one event. Reproduced in actual WASM with measured per-callback fuel through the exact ui_select(BTN_LAUNCH) handler (diagnostic guest, trigger.patch): before, a 111-drone second order at revealed=13172/known=144 exhausted exactly 1e9 and stopped the mod (an 11-drone order at the same state: 247805865); after, the same scenario passes with max command fuel 32946177/1e9, failures=0, and /stats at tick 2602 byte-identical. A stacked single-point layout dies to one warhead (known=0, cheap order), so the scenario deploys spread clusters with a moved player. Fix: one known_units snapshot per order_raid passed to radar_strikes and choose_target; known_units is a single O(log + force) set pass; price_grid_on reuses its snapshot. Emitter/network targeting and carrier choice semantics are unchanged (native equivalence test against the former predicate; WASM checkpoint counters equal). Limits unchanged; HD/recording untouched; wave size unrestricted. Committed regressions: native snapshot-equivalence and second-smart-order tests; the heavy WASM fixture now orders two Smart raids through the wave screen's launch function (airdef preset + airdef raid) with EW and a combat intel log, four accepted orders asserted; the light contract covers preset and the empty-wave refusal. Core release 195 + 8 scenarios; adapter 65 + 1 boundary + 5 scenarios; light WASM 30 steps; heavy WASM 283 steps (tick max 182413686/300M, command max 6939810/1e9, failures 0). Windows GUI acceptance remains external and is not claimed. Co-Authored-By: Kimi Code (Moonshot AI) <noreply@moonshot.cn>
This commit is contained in:
@@ -2391,6 +2391,12 @@ impl Mod for AirDef {
|
||||
self.launch_wave(&mut out)?;
|
||||
None
|
||||
}
|
||||
["preset", n] => {
|
||||
// The wave screen's preset button for scripts: the same apply() the UI cycles.
|
||||
let n = n.parse::<usize>().map_err(|_| "Номер готового состава: число".to_string())?;
|
||||
self.wave.apply(n);
|
||||
Some(format!("Готовый: {}", wave::PRESETS[self.wave.preset].name))
|
||||
}
|
||||
["sites"] => {
|
||||
// The launch sites the radars have found, and the strike missiles (#101).
|
||||
let p = player().ok_or("Нет игрока")?;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
//! stations jammed (its next drones go for them), and from which bearings its
|
||||
//! raids were shot down or got through (its next raid comes from the best).
|
||||
use super::*;
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// How far a station jams.
|
||||
pub const EW_RADIUS: f64 = 350.0;
|
||||
@@ -135,13 +136,17 @@ impl Sim {
|
||||
}
|
||||
|
||||
/// Units the enemy knows of and would go for, newest first: (id, where).
|
||||
/// One pass over the log: aliveness and duplicates by set, not by rescanning
|
||||
/// the lists for every entry — a big log with many systems must not make the
|
||||
/// planner's snapshot quadratic (#122).
|
||||
pub(super) fn known_units(&self) -> Vec<(u32, DVec3)> {
|
||||
let alive: HashSet<u32> = self.sams.iter().filter(|s| s.alive()).map(|s| s.id).collect();
|
||||
let mut seen: HashSet<u32> = HashSet::new();
|
||||
let mut out: Vec<(u32, DVec3)> = Vec::new();
|
||||
for &(id, at, observed) in self.intel.revealed.iter().rev() {
|
||||
if self.tick.saturating_sub(observed) >= 6000 { continue; }
|
||||
if !out.iter().any(|o| o.0 == id) && self.sams.iter().any(|s| s.id == id && s.alive()) {
|
||||
out.push((id, at));
|
||||
}
|
||||
if !seen.insert(id) { continue; }
|
||||
if alive.contains(&id) { out.push((id, at)); }
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
+113
-13
@@ -278,6 +278,10 @@ impl Sim {
|
||||
return 0;
|
||||
}
|
||||
let smart = order.aim == Aim::Smart;
|
||||
// The planner's picture of the layout: one snapshot for the whole order. It is
|
||||
// read-only while the plan is built, and recomputing it per drone made a big
|
||||
// second order quadratic in the intel log and the force's size (#122).
|
||||
let known = self.known_units();
|
||||
// A smart enemy comes from where its raids got through best.
|
||||
let bearing = if smart { self.best_bearing() } else { self.rng.f() * TAU };
|
||||
// The raid's sides (1, 2, or 4-5): its launch sites go there (#100).
|
||||
@@ -290,7 +294,7 @@ impl Sim {
|
||||
let grid = Grid { min: centre - DVec3::new(span, 0.0, span), cell, size: (2.0 * span / cell) as i32 + 1 };
|
||||
let mut items = Vec::new();
|
||||
// A smart raid goes for the radars too (a few strikers each); the others pick as they always did.
|
||||
let radar_jobs = if smart { self.radar_strikes(&order.kinds) } else { Vec::new() };
|
||||
let radar_jobs = if smart { self.radar_strikes(&order.kinds, &known) } else { Vec::new() };
|
||||
let mut index = 0usize;
|
||||
for (group, &(kind, count)) in order.kinds.iter().enumerate() {
|
||||
let spec = kind.spec();
|
||||
@@ -306,7 +310,7 @@ impl Sim {
|
||||
index += 1;
|
||||
let (target, at_sam) = match job {
|
||||
Some((at, id)) => (at, Some(id)),
|
||||
None => self.choose_target(kind, object, order.aim),
|
||||
None => self.choose_target(kind, object, order.aim, &known),
|
||||
};
|
||||
let aim = target + DVec3::new(self.rng.s() * 4.0, 0.0, self.rng.s() * 4.0);
|
||||
let dist = spec.ballistic.map_or(order.dist, |far| far.max(order.dist)) + self.rng.f() * 50.0;
|
||||
@@ -542,8 +546,8 @@ impl Sim {
|
||||
/// The strikes a smart raid sends at observed radars: for drones of `kinds` in order,
|
||||
/// the last observed place and id for those that go for one. Each radar
|
||||
/// gets two or three, the fast strikers first, and all together no more than about a quarter of the raid.
|
||||
pub(super) fn radar_strikes(&self, kinds: &[(DroneKind, u32)]) -> Vec<Option<(DVec3, u32)>> {
|
||||
let mut radars: Vec<(u32, DVec3)> = self.known_units().into_iter()
|
||||
pub(super) fn radar_strikes(&self, kinds: &[(DroneKind, u32)], known: &[(u32, DVec3)]) -> Vec<Option<(DVec3, u32)>> {
|
||||
let mut radars: Vec<(u32, DVec3)> = known.iter().copied()
|
||||
.filter(|(id, _)| self.sams.iter().any(|s| s.id == *id && s.alive() && s.kind == SamKind::Radar))
|
||||
.collect();
|
||||
radars.sort_by_key(|(id, _)| *id);
|
||||
@@ -568,7 +572,7 @@ impl Sim {
|
||||
|
||||
/// What a drone goes for: an object, or (by its kind and the raid's aim) a system. Returns the
|
||||
/// point and the system's id.
|
||||
fn choose_target(&mut self, kind: DroneKind, object: DVec3, aim: Aim) -> (DVec3, Option<u32>) {
|
||||
fn choose_target(&mut self, kind: DroneKind, object: DVec3, aim: Aim, known: &[(u32, DVec3)]) -> (DVec3, Option<u32>) {
|
||||
let alive: Vec<&Sam> = self.sams.iter().filter(|s| s.alive()).collect();
|
||||
if alive.is_empty() || kind.spec().decoy || aim == Aim::Objects {
|
||||
return (object, None);
|
||||
@@ -588,8 +592,7 @@ impl Sim {
|
||||
// station, a radar, a system that fired).
|
||||
// The planner has only what the enemy knows (#93): the radars (they emit) and the units that gave
|
||||
// themselves away; the rest of the player's layout is not in its hands.
|
||||
let known = self.known_units();
|
||||
let alive: Vec<&Sam> = alive.iter().copied().filter(|s| Self::enemy_knows(s, &known)).collect();
|
||||
let alive: Vec<&Sam> = alive.iter().copied().filter(|s| Self::enemy_knows(s, known)).collect();
|
||||
let pick_known = |list: &[&Sam], r: f64| -> Option<(DVec3, u32)> {
|
||||
let s = *list.get((r * list.len() as f64) as usize)?;
|
||||
known.iter().find(|(id, _)| *id == s.id).map(|(_, pos)| (*pos, s.id))
|
||||
@@ -631,9 +634,8 @@ impl Sim {
|
||||
let mut prices = vec![1.0f32; (n * n) as usize];
|
||||
// Known jamming stations: drones that navigate by satellite keep away.
|
||||
if kind.jammable() {
|
||||
let known = self.known_units();
|
||||
for s in self.sams.iter().filter(|s| s.kind == SamKind::Ew && s.alive() && known.iter().any(|k| k.0 == s.id)) {
|
||||
let pos = known.iter().find(|(id, _)| *id == s.id).unwrap().1;
|
||||
for s in self.sams.iter().filter(|s| s.kind == SamKind::Ew && s.alive() && known_now.iter().any(|k| k.0 == s.id)) {
|
||||
let pos = known_now.iter().find(|(id, _)| *id == s.id).unwrap().1;
|
||||
let (ci, cj) = grid.index(pos);
|
||||
let r = (EW_RADIUS / grid.cell).ceil() as i32;
|
||||
for i in (ci - r).max(0)..=(ci + r).min(n - 1) {
|
||||
@@ -979,16 +981,19 @@ mod tests {
|
||||
radar.radar_on = false;
|
||||
sim.sams.push(radar);
|
||||
let kinds = [(DroneKind::Kh22, 8)];
|
||||
assert!(sim.radar_strikes(&kinds).iter().all(Option::is_none));
|
||||
let none = Vec::new();
|
||||
assert!(sim.radar_strikes(&kinds, &none).iter().all(Option::is_none));
|
||||
sim.tick = 20;
|
||||
sim.intel.revealed.push((1, old, sim.tick));
|
||||
sim.sams[0].pos = DVec3::new(900.0, 70.0, 900.0);
|
||||
sim.tick = 40;
|
||||
let jobs = sim.radar_strikes(&kinds);
|
||||
let known = sim.known_units();
|
||||
let jobs = sim.radar_strikes(&kinds, &known);
|
||||
assert_eq!(jobs.iter().flatten().count(), 2);
|
||||
assert!(jobs.iter().flatten().all(|job| *job == (old, 1)));
|
||||
sim.tick = 6020;
|
||||
assert!(sim.radar_strikes(&kinds).iter().all(Option::is_none));
|
||||
let known = sim.known_units();
|
||||
assert!(sim.radar_strikes(&kinds, &known).iter().all(Option::is_none));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1044,6 +1049,101 @@ mod tests {
|
||||
check(&sim, "after adapting");
|
||||
}
|
||||
|
||||
/// The snapshot the planner works from must not depend on how it is computed:
|
||||
/// the set-based pass is checked here against the former per-entry rescan, over
|
||||
/// duplicates, the dead, the aged-out, ids the force never had, and ordering.
|
||||
#[test]
|
||||
fn known_units_matches_the_naive_reference_on_a_fat_log() {
|
||||
let mut sim = Sim::new(9);
|
||||
let mut places = Vec::new();
|
||||
for (i, kind) in [SamKind::S400, SamKind::Ew, SamKind::Radar, SamKind::Tor, SamKind::Patriot].into_iter().enumerate() {
|
||||
let id = sim.id();
|
||||
let mut sam = Sam::new(id, kind, DVec3::new(i as f64 * 100.0, 70.0, -50.0), 0.0);
|
||||
// The second system is dead: known only while it lived.
|
||||
if i == 1 { sam.health = 0.0; }
|
||||
sim.sams.push(sam);
|
||||
places.push((id, DVec3::new(i as f64 * 100.0, 70.0, -50.0)));
|
||||
}
|
||||
let naive = |sim: &Sim| {
|
||||
let mut out: Vec<(u32, DVec3)> = Vec::new();
|
||||
for &(id, at, observed) in sim.intel.revealed.iter().rev() {
|
||||
if sim.tick.saturating_sub(observed) >= 6000 { continue; }
|
||||
if !out.iter().any(|o| o.0 == id) && sim.sams.iter().any(|s| s.id == id && s.alive()) {
|
||||
out.push((id, at));
|
||||
}
|
||||
}
|
||||
out
|
||||
};
|
||||
sim.tick = 7000;
|
||||
let dead = places[1].0;
|
||||
let (a, b, c, d, e) = (places[0].0, places[1].0, places[2].0, places[3].0, places[4].0);
|
||||
// A long log: repeated observations (newest wins), one dead system, one never-seen
|
||||
// system, one id the force does not have, and an entry that aged out.
|
||||
for t in 0..200u64 {
|
||||
sim.intel.revealed.push((a, DVec3::new(11.0, 70.0, t as f64), 1000 + t));
|
||||
sim.intel.revealed.push((c, DVec3::new(13.0, 70.0, t as f64), 1000 + t));
|
||||
if t % 7 == 0 { sim.intel.revealed.push((b, DVec3::new(12.0, 70.0, t as f64), 1000 + t)); }
|
||||
if t % 11 == 0 { sim.intel.revealed.push((d, DVec3::new(14.0, 70.0, t as f64), 1000 + t)); }
|
||||
if t % 13 == 0 { sim.intel.revealed.push((999_999, DVec3::new(1.0, 70.0, 0.0), 1000 + t)); }
|
||||
}
|
||||
sim.intel.revealed.push((e, DVec3::new(15.0, 70.0, 0.0), 2)); // aged out at tick 7000
|
||||
let expected = naive(&sim);
|
||||
assert_eq!(sim.known_units(), expected);
|
||||
assert_eq!(expected.iter().map(|k| k.0).collect::<Vec<_>>(), vec![c, a, d]);
|
||||
assert!(expected.iter().all(|k| k.0 != dead && k.0 != e && k.0 != 999_999));
|
||||
// The newest observation's place is the one kept.
|
||||
assert_eq!(expected.iter().find(|k| k.0 == a).unwrap().1, DVec3::new(11.0, 70.0, 199.0));
|
||||
}
|
||||
|
||||
/// The reported Windows stop (#122): the second big order from the UI path, with a fat
|
||||
/// intel log and a large surviving force, must be accepted and launched like the first.
|
||||
/// (The old per-drone planner snapshot was correct but quadratic; its cost is pinned by
|
||||
/// the actual-WASM fuel measurements in docs/measurements/issue-122-ui-fuel.)
|
||||
#[test]
|
||||
fn a_second_smart_order_with_a_fat_intel_log_is_accepted_and_launched() {
|
||||
let mut sim = Sim::new(31);
|
||||
let mut ids = Vec::new();
|
||||
for i in 0..48 {
|
||||
let id = sim.id();
|
||||
sim.sams.push(Sam::new(id, [SamKind::S400, SamKind::Patriot, SamKind::Nasams, SamKind::Pantsir][i % 4], DVec3::new((i % 8 * 40) as f64, 70.0, (i / 8 * 40) as f64), 0.0));
|
||||
ids.push(id);
|
||||
}
|
||||
// A fought-off first raid leaves a long log: launches, emitting radars, stations.
|
||||
// Its entries sit in the past of the sim clock, like real ones.
|
||||
sim.tick = 4000;
|
||||
for t in 0..3000u64 {
|
||||
sim.intel.revealed.push((ids[(t as usize) % ids.len()], DVec3::new(10.0, 70.0, t as f64), 1001 + t));
|
||||
}
|
||||
sim.targets.push(DVec3::new(160.0, 70.0, 160.0));
|
||||
sim.target_health.push(100.0);
|
||||
let mut ground = Ground::default();
|
||||
let land = Land(|_, _| 64);
|
||||
let mut out = Out::default();
|
||||
for _ in 0..20 {
|
||||
serve(&mut ground, &land, sim.tick);
|
||||
sim.step(&mut ground, None, &mut out);
|
||||
}
|
||||
let mut order = RaidOrder::mix(36, 300.0);
|
||||
order.aim = Aim::Smart;
|
||||
order.approach = Approach::AllSides;
|
||||
let total: u32 = order.kinds.iter().map(|k| k.1).sum();
|
||||
let first = sim.order_raid(&order, &sim.targets.clone(), &mut out);
|
||||
assert_eq!(first, total);
|
||||
while sim.planning() {
|
||||
serve(&mut ground, &land, sim.tick);
|
||||
sim.step(&mut ground, None, &mut out);
|
||||
}
|
||||
assert!(sim.queue.len() + sim.launched as usize >= total as usize, "the first order queued or launched every drone (queue {}, launched {})", sim.queue.len(), sim.launched);
|
||||
// The second order — the path that stopped the mod on Windows — is accepted too.
|
||||
let second = sim.order_raid(&order, &sim.targets.clone(), &mut out);
|
||||
assert_eq!(second, total);
|
||||
while sim.planning() {
|
||||
serve(&mut ground, &land, sim.tick);
|
||||
sim.step(&mut ground, None, &mut out);
|
||||
}
|
||||
assert!(sim.queue.len() + sim.launched as usize >= 2 * total as usize, "both orders queued or launched their drones (queue {}, launched {})", sim.queue.len(), sim.launched);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn systems_ready_together_send_one_missile_at_a_drone() {
|
||||
let mut sim = Sim::new(3);
|
||||
|
||||
@@ -1017,3 +1017,48 @@ frames = 10
|
||||
[[step]]
|
||||
command = "airdef stats"
|
||||
expect.feedback = "В воздухе: 184, ракет: 10, ПВО: 64, сбито: 777, поражений: 1499, разбились о препятствия: 0; удары по ПУ: пущено 0, уничтожено 0, подавлено 0, промахов 0, сбито ПВО 0; мир: 5353 секций (250 КБ), запросов 14405, неизвестно 0, лучей 327428 (ударов 6746, неизв. 213), клеток 2282312; взрывы в блоках: 1586 (подтверждено 0, без перемен 175, нет чанка 1330), чужих ответов 0"
|
||||
|
||||
# The Windows #122 path: a Smart order through the UI launch function (`airdef raid`
|
||||
# is the wave screen's ПУСК button handler) with a fat intel log and EW up — twice.
|
||||
[[step]]
|
||||
command = "airdef deploy ew"
|
||||
|
||||
[[step]]
|
||||
command = "airdef deploy ew"
|
||||
|
||||
[[step]]
|
||||
command = "airdef deploy ew"
|
||||
|
||||
[[step]]
|
||||
command = "airdef deploy ew"
|
||||
|
||||
[[step]]
|
||||
command = "airdef ew on"
|
||||
|
||||
[[step]]
|
||||
command = "airdef preset 1"
|
||||
expect.feedback = "Готовый: Массированный удар"
|
||||
|
||||
[[step]]
|
||||
command = "airdef raid"
|
||||
expect.chat = []
|
||||
|
||||
[[step]]
|
||||
ticks = 400
|
||||
frames = 50
|
||||
|
||||
[[step]]
|
||||
command = "airdef preset 1"
|
||||
expect.feedback = "Готовый: Массированный удар"
|
||||
|
||||
[[step]]
|
||||
command = "airdef raid"
|
||||
expect.chat = []
|
||||
|
||||
[[step]]
|
||||
ticks = 800
|
||||
frames = 100
|
||||
|
||||
[[step]]
|
||||
command = "airdef stats"
|
||||
expect.feedback = "В воздухе: 11, ракет: 3, ПВО: 68, сбито: 936, поражений: 1853, разбились о препятствия: 0; удары по ПУ: пущено 0, уничтожено 0, подавлено 0, промахов 0, сбито ПВО 0; мир: 6643 секций (311 КБ), запросов 17355, неизвестно 0, лучей 393828 (ударов 9283, неизв. 978), клеток 2953257; взрывы в блоках: 1961 (подтверждено 0, без перемен 234, нет чанка 1727), чужих ответов 0"
|
||||
|
||||
@@ -73,6 +73,19 @@ expect.error = "У комплекса нет активной РЛС"
|
||||
[[step]]
|
||||
command = "airdef deploy s500_launcher"
|
||||
|
||||
# The wave screen's preset button through the command boundary; an empty wave cannot launch.
|
||||
[[step]]
|
||||
command = "airdef preset 0"
|
||||
expect.feedback = "Готовый: Рой Шахедов"
|
||||
|
||||
[[step]]
|
||||
command = "airdef preset 6"
|
||||
expect.feedback = "Готовый: Пусто"
|
||||
|
||||
[[step]]
|
||||
command = "airdef raid"
|
||||
expect.error = "Налёт пуст: выберите типы и число"
|
||||
|
||||
[[step]]
|
||||
command = "airdef wave mix 40 1500"
|
||||
|
||||
|
||||
+34
-2
@@ -16,6 +16,37 @@
|
||||
подтвердила 1.6 как фактический голос P1-SUN (недостижимый `P1sun => 1.7` в старом match не в счёт);
|
||||
на это добавлена Rust-регрессия. Остаток: варианты комплексов и игровая приёмка.
|
||||
|
||||
### Продолжение #122 (второй налёт из UI)
|
||||
|
||||
Точный лог Windows: `stopped in UI event (fuel budget 1000000000, remaining Some(0))`,
|
||||
backtrace `known_units ← order_raid ← launch_wave ← ui_select` — другой класс, чем тик
|
||||
`1f28f83`. Причина: `order_raid` пересчитывал снимок известных комплексов на каждом дроне,
|
||||
а сам снимок был квадратичен по журналу и группировке. Исправление: один снимок на приказ
|
||||
(+ O(журнал+группировка) через множества, `price_grid_on` переиспользует свой снимок);
|
||||
семантика целеуказания и выбора носителей не менялась, сверка с прежним предикатом в
|
||||
нативном тесте, `/stats` в WASM совпал побайтово. Лимиты не трогали, HD/запись не отключали,
|
||||
размер волны не ограничивали.
|
||||
|
||||
Воспроизведение в настоящем WASM (измеренный fuel): до — второй приказ 111 дронов исчерпывает
|
||||
ровно 1e9 в событии (failures=1), тот же приказ 11 дронов — 247805865; после — тот же сценарий
|
||||
целиком: max command 32946177/1e9, tick 236581288/300M, ошибок 0. Диагностический гость
|
||||
(`trigger.patch` вызывает обработчик `ui_select(BTN_LAUNCH)`) и точный сценарий:
|
||||
[issue-122-ui-fuel](measurements/issue-122-ui-fuel/summary.json). Воспроизвести удалось только
|
||||
с разнесённой группировкой: стек в одной точке гибнет от одной боеголовки (`known=0`, приказ
|
||||
дёшев) — у пользователя силы дожили до второго приказа.
|
||||
|
||||
Закреплённые регрессии: нативные «эквивалентность снимка» и «второй умный приказ с толстым
|
||||
журналом принят и запущен»; тяжёлый WASM fixture расширен двумя умными приказами через
|
||||
`airdef preset` + `airdef raid` (функция кнопки ПУСК) — 4 принятых приказа, SHA fixture
|
||||
обновлён; лёгкий контракт 30 шагов с `preset` и отказом пустой волны. Новая команда
|
||||
`airdef preset <n>` — script-эквивалент кнопки готового состава.
|
||||
|
||||
Последний общий прогон: core release **195 + 8 сценариев** / 7 ignored; адаптер **65 + 1
|
||||
boundary + 5 сценариев**; лёгкий WASM **30 шагов**; тяжёлый WASM **283 шага** (tick max
|
||||
182413686/300M, command max 6939810/1e9, failures=0), отчёт
|
||||
[flat-wasm-raid-budget](measurements/flat-wasm-raid-budget.json); 6 тестов отчёта budget
|
||||
и build-тест прошли. Ни одно issue не закрыто; пуш не выполнялся.
|
||||
|
||||
### Контрольная точка 3 ноября
|
||||
|
||||
Код предыдущих четырёх задач интегрирован до `6e2d661`. Продолжение #102/обратного вывоза
|
||||
@@ -76,8 +107,9 @@ awaiting-backend. Выход через quit подтверждён. Это не
|
||||
|
||||
## Двойные налёты и плотность следов
|
||||
|
||||
Перед продолжением читать [RAID-STABILITY.md](RAID-STABILITY.md): один соответствующий класс
|
||||
отключения теперь воспроизведён и исправлен (`1f28f83`), приёмка конкретного Windows-сеанса остаётся.
|
||||
Перед продолжением читать [RAID-STABILITY.md](RAID-STABILITY.md): два соответствующих класса
|
||||
отключения теперь воспроизведены и исправлены — тик (`1f28f83`) и событие UI на втором
|
||||
приказе (#122, `order_raid`/known_units); приёмка конкретного Windows-сеанса остаётся.
|
||||
Текущий исправленный WASM stress проверяет две принятые волны, обслуживаемую плоскую геометрию
|
||||
и 184 воздушные цели в конкретный момент; новый [JSON](measurements/flat-wasm-raid-budget.json)
|
||||
заменяет прежнее недостаточное доказательство покрытия. Лимиты fuel/memory не увеличивались.
|
||||
|
||||
+21
-1
@@ -122,6 +122,13 @@ GPU не имитируется. Проверяются два принятых
|
||||
frame hook; эти вызовы не являются нагрузкой на GPU или измерением 10M frame fuel мода.
|
||||
Точный отчёт: [flat-world budget](measurements/flat-wasm-raid-budget.json).
|
||||
|
||||
С 4 ноября (#122) fixture расширен хвостом умных приказов через UI-путь: 4 РЭБ, `airdef preset 1`
|
||||
(та же `apply()`, что у кнопки готового состава) и два `airdef raid` — это та же функция
|
||||
`launch_wave`, что у кнопки ПУСК (`ui_select`), с боевым журналом разведки и EW; оба приказа
|
||||
приняты (`expect.chat = []`), итого 4 принятых приказа, финальный `/stats` закреплён строкой.
|
||||
Прогон исправления #122: 283 шага, 3302 tick; max fuel tick 182413686/300M, command 6939810/1e9,
|
||||
ошибок 0. Отчёт: [flat-world budget](measurements/flat-wasm-raid-budget.json).
|
||||
|
||||
Fixture копируется из тех же байтов, чей SHA записан до сборки. Проверенные 2 принятия и точка
|
||||
184/2102 заносятся только после success и совпадения `BUDGET_ASSERTED_SHA256` с точной версией файла.
|
||||
После изменения fixture нужно повторно проверить её и обновить SHA/метаданные; простой список
|
||||
@@ -137,11 +144,15 @@ API/WIT/артефакт, проверку build identity, сценарий, Was
|
||||
Отсутствие запрошенных счётчиков делает проверку неуспешной, даже если сценарий завершился.
|
||||
Это не время host/GPU и не peak memory; невызывавшиеся callbacks, включая init до включения профиля,
|
||||
не получают выдуманного нуля. Первый прогон: tick 2102 вызова, max 106132646/300000000;
|
||||
command 69 вызовов, max 1013370/1000000000; ошибок нет.
|
||||
command 69 вызовов, max 1013370/1000000000; ошибок нет. Прогон исправления #122 (283 шага,
|
||||
включая два умных UI-приказа): tick 3302 вызова, max 182413686/300M; command 79 вызовов,
|
||||
max 6939810/1e9; ошибок нет.
|
||||
Исторический отчёт без измерения fuel:
|
||||
[API27 budget report с поправкой покрытия](measurements/api27-wasm-budget.json).
|
||||
Актуальный чистый API29-прогон с измерением:
|
||||
[271 шаг, fuel и build identity](measurements/api29-wasm-budget.json).
|
||||
Актуальный прогон с умными UI-приказами #122 и build identity:
|
||||
[flat-world budget](measurements/flat-wasm-raid-budget.json).
|
||||
|
||||
Проверены реальный WASM guest с переходами состояний/владельца/мира, сборка клиента и два коротких
|
||||
offscreen-профиля Metal. При `HORIZON_ACOUSTICS=1` + `HORIZON_NO_AUDIO=1` в игре получено
|
||||
@@ -234,3 +245,12 @@ GPU/готовый MP4 и платформенная приёмка. Эти ре
|
||||
3602 тика прошли, max tick fuel = 135273690/300M, failures=0. Лимиты не менялись.
|
||||
Диагностический гость содержит только временный способ запуска точного меню-состава;
|
||||
это не исходный пользовательский Windows-сеанс. [Данные и воспроизведение](RAID-STABILITY.md).
|
||||
|
||||
С профилем воспроизведён и второй класс — остановка в **событии UI** (не тике) на втором
|
||||
крупном приказе: переснимок `known_units` на каждом дроне приказа давал `дроны × журнал ×
|
||||
группировка` за одно событие, 1e9 исчерпаны ровно (max command fuel 1000000000/1e9,
|
||||
failures=1; состояние: 13172 записей журнала, 144 известных живых комплекса). Исправление
|
||||
(#122, одним снимком на приказ и O(журнал+группировка) за проход) доводит тот же сценарий
|
||||
до max command fuel 32946177/1e9, failures=0, `/stats` на тике 2602 совпал побайтово.
|
||||
Это гостевые инструкции, не прирост FPS; приёмка Windows-сеанса внешняя.
|
||||
[Данные и воспроизведение](RAID-STABILITY.md).
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
| #119 | Три read-only луча в известной геометрии ослабляют непрямой blast для корпуса/узлов/объектов; проёмы дают частичное воздействие; прямой контакт сохранён | Unknown/stale: прежний урон без защиты, нулевое ожидание; partial blocks считаются занятыми кубами; GUI-проверка разрушения укрытия ещё нужна |
|
||||
| #120 | Конечное горение, powered/coast, сопротивление и снижение доступного манёвра, один остаточный pk по фактической скорости; swept-встреча; события burnout/energy loss; согласованное прекращение факела/дыма/звука | Игровой профиль, без аэродинамики и реальных параметров; сравнительная выборка 12 рейдов и расширенная сетка 48 рейдов (6 вариантов × 8 seed, батареи/дальности/классы) записаны в measurements; без подтверждённого дефекта параметры не менялись; прослушивание остаётся |
|
||||
| #121 | On/Off в команде и карте, save/replay; Off не создаёт own measurements и сохраняет сетевой приём; раскрытие работающего радара наблюдателем; SEAD использует last-known и возраст | Условный наблюдатель по расстоянию, без радиотехнической модели и ошибки пеленга; выбор на карте ещё не проверен в GUI |
|
||||
| #122 | Воспроизведён tick fuel exhaustion на двух максимальных составах, HD; полный обход height-cache на каждом взрыве заменён удалением нужных ключей; тест эквивалентности и WASM421/3602 прошли, max135.3M/300M; callback diagnostics/profile | Требуется приёмка на Windows; без старого лога нельзя подтвердить ту же причину пользовательского сеанса; FPS/GPU не измерялись |
|
||||
| #122 | Воспроизведены два класса: tick fuel exhaustion на двух максимальных составах, HD (полный обход height-cache на каждом взрыве заменён удалением нужных ключей; max135.3M/300M) и остановка в UI event на втором крупном приказе (переснимок known_units на каждом дроне заменён одним снимком на приказ и O(журнал+группировка); max command 32.9M/1e9, `/stats` до/после совпал); нативные тесты эквивалентности и второго приказа; WASM fixture с 4 принятыми приказами; callback diagnostics/profile | Требуется приёмка на Windows; без подтверждённого build identity нельзя утверждать ту же причину пользовательского сеанса; FPS/GPU не измерялись |
|
||||
|
||||
Точные коммиты, формат сохранения, тесты и порядок продолжения:
|
||||
[CODEX-HANDOFF.md](CODEX-HANDOFF.md). Формальный open-статус не меняется после реализации.
|
||||
|
||||
+51
-3
@@ -7,10 +7,58 @@
|
||||
поэтому запись может сохраниться и без ручного копирования.
|
||||
Задача: [#122](https://gitea.maoniao.team/chexit/airdef/issues/122).
|
||||
|
||||
**Воспроизведена и исправлена одна конкретная причина отключения при таком классе нагрузки.**
|
||||
Без старого лога не утверждается, что Windows-сеанс имел именно её; GPU/FPS отдельно не измерялись.
|
||||
**Воспроизведены и исправлены две конкретные причины отключения в этом классе нагрузки.**
|
||||
Первая — тик и кэш высот (`1f28f83`, ниже). Вторая — событие UI и второй приказ
|
||||
(`order_raid`, см. раздел «Второй налёт из UI: событие, не тик»). Без старого лога не
|
||||
утверждается, что Windows-сеанс имел именно их; GPU/FPS отдельно не измерялись.
|
||||
|
||||
## Найденная причина и фикс
|
||||
## Второй налёт из UI: событие, не тик
|
||||
|
||||
Точный лог Windows: `Horizon: mod airdef stopped in UI event (fuel budget 1000000000,
|
||||
remaining Some(0))`; backtrace: `ew::Sim::known_units` ← `raid::Sim::order_raid` ←
|
||||
`airdef::AirDef::launch_wave` ← `ui_select`. Прежний fix `1f28f83` — другой класс: тогда
|
||||
мод останавливался в **тике** на 300M.
|
||||
|
||||
**Причина.** `order_raid` для каждого дрона заново вычислял снимок известных комплексов
|
||||
`known_units()` (`choose_target`), а сам `known_units()` на каждую запись журнала разведки
|
||||
пересканировал весь список комплексов и список уже принятых. Журнал держит каждый пуск,
|
||||
каждое раскрытие РЭБ и каждую emitting-РЛС за последние 6000 тиков. Стоимость второго
|
||||
большого приказа росла как `дроны × журнал × размер группировки` внутри одного UI-события.
|
||||
|
||||
**Воспроизведение** (настоящий WASM, измеренный fuel по callback, диагностический гость —
|
||||
`trigger.patch` гоняет тот же обработчик `ui_select(BTN_LAUNCH)` → `launch_wave` →
|
||||
`order_raid` с максимальным составом меню; метка callback — `command`, бюджет тот же 1e9):
|
||||
разнесённая группировка 172 комплекса (40 кластеров × 4 тяжёлых + 12 РЭБ, игрок двигается
|
||||
шагами сценария — стек в одной точке умирает от одной боеголовки и не воспроизводит),
|
||||
первый налёт 111 дронов отбит, второй приказ на тике ~2600 при `revealed=13172`,
|
||||
`known=144` живых. До fix: второй приказ исчерпывает ровно 1e9 и останавливает мод
|
||||
(`before-trap.txt`); тот же приказ из 11 дронов в том же состоянии — 247805865
|
||||
(`before-scaling.txt`). После fix тот же сценарий проходит целиком: max command fuel
|
||||
32946177/1e9, max tick 236581288/300M, ошибок 0; `/stats` на тике 2602 совпал побайтово
|
||||
до/после, включая счётчики мира.
|
||||
|
||||
**Фикс.** Один снимок `known_units` на весь приказ, передаётся в `radar_strikes` и
|
||||
`choose_target`; сам `known_units` — один проход O(журнал + группировка) через множества;
|
||||
`price_grid_on` переиспользует свой снимок. Семантика планирования не изменилась:
|
||||
нативный тест сверяет новый снимок с прежним предикатом (дубликаты, погибшие, устаревшие,
|
||||
чужие id, порядок «новые первыми»). Лимиты не менялись, HD/запись не отключались,
|
||||
размер волны не ограничивался.
|
||||
|
||||
**Регрессии.** Нативные: эквивалентность снимка и «второй умный приказ с толстым журналом
|
||||
принят и запущен». Закреплённые WASM: тяжёлый fixture расширен — два умных приказа через
|
||||
функцию кнопки ПУСК (`airdef preset 1` + `airdef raid` — тот же `launch_wave`, что и у
|
||||
`ui_select`) с РЭБ и боевым журналом, оба приняты (`expect.chat = []`), 4 принятых приказа
|
||||
в отчёте; лёгкий контракт проверяет `preset` и отказ пустой волны. Данные и точный
|
||||
сценарий: [summary.json](measurements/issue-122-ui-fuel/summary.json),
|
||||
[trigger.patch](measurements/issue-122-ui-fuel/trigger.patch),
|
||||
[до](measurements/issue-122-ui-fuel/scenario-before.toml),
|
||||
[после](measurements/issue-122-ui-fuel/scenario-after.toml). Patch — только для
|
||||
диагностического гостя, в мод не входит.
|
||||
|
||||
Приёмка конкретного Windows-сеанса остаётся внешней: нужны build identity и строки
|
||||
`latest.log` вокруг остановки. Измерение fuel — не прирост FPS.
|
||||
|
||||
## Первая причина и фикс (тик, `1f28f83`)
|
||||
|
||||
Два максимальных состава меню (11 видов × 40, два принятых приказа), 64 ПВО, расстояние 300,
|
||||
HD и grid, обслуживаемый плоский мир: tick 1835 исчерпал ровно 300M fuel и мод остановился.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"airdef": {
|
||||
"diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"dirty": false,
|
||||
"sha": "293236490b567866f7468a5bee5d61b0dcf8086d"
|
||||
"diff_sha256": "143fbab5f1fd60981df9ef8ef3081b08a4102507f2588c6942924a7325b5139b",
|
||||
"dirty": true,
|
||||
"sha": "e6a70004406b34ea17884875ce236a5fb4b849e4"
|
||||
},
|
||||
"api_level": 27,
|
||||
"artifact_sha256": "cb52fa5fd6beb266c63f2a5f2aa50553c24bc435a212c76142cc1bd228fd0aa6",
|
||||
"api_level": 29,
|
||||
"artifact_sha256": "c4ef7d759524eb053ccd55f0eccdcb49c91954201eb4e0ad923ee9b2828efc4e",
|
||||
"build_identity": {
|
||||
"checks": {
|
||||
"airdef_diff_sha256": true,
|
||||
@@ -17,15 +17,45 @@
|
||||
"wit_sha256": true
|
||||
},
|
||||
"matches": true,
|
||||
"path": "airdef-mod/target/horizon-mod/build-identity.json"
|
||||
"path": "/private/tmp/issue-122-raid/airdef-mod/target/horizon-mod/build-identity.json"
|
||||
},
|
||||
"command": "test-budgets",
|
||||
"engine": {
|
||||
"identity": "ed7615d362490f57bca5ac239c64eb9360913a4b",
|
||||
"pin": "ed7615d362490f57bca5ac239c64eb9360913a4b"
|
||||
"identity": "73b9d307048fc44d7f21fa3c829fc5a855d144a6",
|
||||
"pin": "73b9d307048fc44d7f21fa3c829fc5a855d144a6"
|
||||
},
|
||||
"exit_code": 0,
|
||||
"output": " Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.39s\n Running `<checkout>/horizon/target/debug/cargo-horizon-mod horizon-mod test --hmod <checkout>/airdef-mod/target/horizon-mod/airdef.hmod budget`\nrunning 1 scripts against <checkout>/airdef-mod/target/horizon-mod/airdef.hmod\ntest tests/wasm_budget.toml ... ok (271 steps)\ntest result: ok. 1 passed\n",
|
||||
"measured_fuel": {
|
||||
"available": true,
|
||||
"callbacks": [
|
||||
{
|
||||
"budget": 1000000000,
|
||||
"callback": "command",
|
||||
"calls": 79,
|
||||
"failures": 0,
|
||||
"max_used": 6939810,
|
||||
"mod": "airdef"
|
||||
},
|
||||
{
|
||||
"budget": 10000000,
|
||||
"callback": "frame",
|
||||
"calls": 2202,
|
||||
"failures": 0,
|
||||
"max_used": 77,
|
||||
"mod": "airdef"
|
||||
},
|
||||
{
|
||||
"budget": 300000000,
|
||||
"callback": "tick",
|
||||
"calls": 3302,
|
||||
"failures": 0,
|
||||
"max_used": 182413686,
|
||||
"mod": "airdef"
|
||||
}
|
||||
],
|
||||
"scope": "guest callback fuel; excludes host CPU, memory and GPU time"
|
||||
},
|
||||
"output": " Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.35s\n Running `/private/tmp/issue-122-raid/horizon/target/debug/cargo-horizon-mod horizon-mod test --hmod /private/tmp/issue-122-raid/airdef-mod/target/horizon-mod/airdef.hmod --fuel-profile budget`\nrunning 1 scripts against /private/tmp/issue-122-raid/airdef-mod/target/horizon-mod/airdef.hmod\nfuel airdef command: calls=79 max=6939810 budget=1000000000 failures=0\nfuel airdef frame: calls=2202 max=77 budget=10000000 failures=0\nfuel airdef tick: calls=3302 max=182413686 budget=300000000 failures=0\ntest tests/wasm_budget.toml ... ok (283 steps)\ntest result: ok. 1 passed\n",
|
||||
"runtime_limits": {
|
||||
"fuel_per_call": {
|
||||
"event": 1000000000,
|
||||
@@ -34,16 +64,16 @@
|
||||
"tick": 300000000
|
||||
},
|
||||
"memory_bytes": 67108864,
|
||||
"metering": "source_configured; measured_per_scenario: unavailable",
|
||||
"metering": "source_configured",
|
||||
"source": "horizon/crates/horizon-mods/src/runtime.rs",
|
||||
"wasmtime_component_model": true,
|
||||
"wasmtime_fuel_metering": true
|
||||
},
|
||||
"scenario": "tests/budgets/wasm_flat_budget.toml",
|
||||
"scenario_counts": {
|
||||
"accepted_raids_asserted": 2,
|
||||
"air_defense_units": 64,
|
||||
"frames": 2052,
|
||||
"accepted_raids_asserted": 4,
|
||||
"air_defense_units": 68,
|
||||
"frames": 2202,
|
||||
"layout_unit_limit": 2000,
|
||||
"peak_airborne_observed": null,
|
||||
"raid_requests": 2,
|
||||
@@ -51,13 +81,13 @@
|
||||
"scripts": 1,
|
||||
"snapshot_airborne_asserted": 184,
|
||||
"snapshot_tick": 2102,
|
||||
"steps": 271,
|
||||
"ticks": 2102
|
||||
"steps": 283,
|
||||
"ticks": 3302
|
||||
},
|
||||
"scenario_render_world": "flat at y=64",
|
||||
"scenario_sha256": "bcc0359289ab20ab3de608bcc130f72d2cf88f9f6fbc470d7b6846e9db5d11df",
|
||||
"scenario_sha256": "134cb4667691cf9faa639ed37120941d829cfc13ce45e9638582c409d9e0dd29",
|
||||
"schema": 1,
|
||||
"status": "passed",
|
||||
"wasmtime_version": "48.0.7",
|
||||
"wit_sha256": "14c57b73f144888e04e27b77a6dfa29098c6b0c27c0383bb96aedaccb4c0a1d4"
|
||||
"wit_sha256": "dded023a150a5f916bc5d935b7f891806728e0118eb7107ad0551d2aad8c70a5"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
running 1 scripts against /tmp/issue-122-raid/airdef-mod/target/horizon-mod/airdef.hmod
|
||||
fuel airdef command: calls=179 max=32946177 budget=1000000000 failures=0
|
||||
fuel airdef frame: calls=377 max=77 budget=10000000 failures=0
|
||||
fuel airdef tick: calls=3802 max=236581288 budget=300000000 failures=0
|
||||
test tests/wasm_budget.toml ... ok (255 steps)
|
||||
test result: ok. 1 passed
|
||||
@@ -0,0 +1,10 @@
|
||||
=== BEFORE: scaling witness — an 11-drone second order at the same state ===
|
||||
running 1 scripts against /tmp/issue-122-run/airdef-diag-before.hmod
|
||||
fuel airdef command: calls=177 max=247805865 budget=1000000000 failures=0
|
||||
fuel airdef frame: calls=257 max=77 budget=10000000 failures=0
|
||||
fuel airdef tick: calls=2602 max=241372671 budget=300000000 failures=0
|
||||
test tests/wasm_budget.toml ... FAILED
|
||||
step 229 (/airdef wave mix 1 300): feedback
|
||||
expected: Some("PROBE")
|
||||
got: Some("DIAG revealed=13172 known=144 sams=172 alive=144 planning=true queue=0")
|
||||
error: 1 failed, 0 passed
|
||||
@@ -0,0 +1,7 @@
|
||||
=== BEFORE: second order (UI path) traps at the 1e9 event budget ===
|
||||
running 1 scripts against /tmp/issue-122-run/airdef-diag-before.hmod
|
||||
fuel airdef command: calls=177 max=1000000000 budget=1000000000 failures=1
|
||||
fuel airdef frame: calls=257 max=77 budget=10000000 failures=0
|
||||
fuel airdef tick: calls=2602 max=241372671 budget=300000000 failures=0
|
||||
test tests/wasm_budget.toml ... ok (229 steps)
|
||||
test result: ok. 1 passed
|
||||
@@ -0,0 +1,762 @@
|
||||
[[step]]
|
||||
render-ground-y = 64
|
||||
ticks = 2
|
||||
frames = 2
|
||||
expect.commands = ["airdef"]
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 40.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 40.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 40.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 40.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
command = "airdef ew on"
|
||||
[[step]]
|
||||
command = "airdef wave mix 10 300"
|
||||
[[step]]
|
||||
ticks = 100
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
command = "airdef ew on"
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
command = "airdef ew on"
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
command = "airdef stats"
|
||||
expect.feedback = "В воздухе: 0, ракет: 0, ПВО: 172, сбито: 308, поражений: 87, разбились о препятствия: 0; удары по ПУ: пущено 0, уничтожено 0, подавлено 0, промахов 0, сбито ПВО 0; мир: 8156 секций (382 КБ), запросов 16075, неизвестно 0, лучей 87524 (ударов 1830, неизв. 1567), клеток 1318224; взрывы в блоках: 239 (подтверждено 0, без перемен 37, нет чанка 202), чужих ответов 0"
|
||||
[[step]]
|
||||
command = "airdef wave mix 10 300"
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
command = "airdef stats"
|
||||
expect.feedback = "В воздухе: 0, ракет: 0, ПВО: 172, сбито: 309, поражений: 87, разбились о препятствия: 0; удары по ПУ: пущено 0, уничтожено 0, подавлено 0, промахов 0, сбито ПВО 0; мир: 8557 секций (401 КБ), запросов 16511, неизвестно 0, лучей 89230 (ударов 2855, неизв. 2039), клеток 1326528; взрывы в блоках: 239 (подтверждено 0, без перемен 37, нет чанка 202), чужих ответов 0"
|
||||
@@ -0,0 +1,688 @@
|
||||
[[step]]
|
||||
render-ground-y = 64
|
||||
ticks = 2
|
||||
frames = 2
|
||||
expect.commands = ["airdef"]
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 0.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 0.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 0.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 0.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 0.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 40.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 40.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 40.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 40.0 }
|
||||
command = "airdef deploy ew"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 40.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 40.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 40.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 40.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 80.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 80.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 80.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 80.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 120.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 120.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 120.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 120.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 0.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 40.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 80.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 120.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 160.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 200.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 240.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 160.0 }
|
||||
command = "airdef deploy s400"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 160.0 }
|
||||
command = "airdef deploy patriot"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 160.0 }
|
||||
command = "airdef deploy nasams"
|
||||
[[step]]
|
||||
player = { x = 280.0, z = 160.0 }
|
||||
command = "airdef deploy pantsir"
|
||||
[[step]]
|
||||
command = "airdef ew on"
|
||||
[[step]]
|
||||
command = "airdef wave mix 10 300"
|
||||
[[step]]
|
||||
ticks = 100
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
command = "airdef ew on"
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
command = "airdef ew on"
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
ticks = 50
|
||||
frames = 5
|
||||
[[step]]
|
||||
command = "airdef stats"
|
||||
expect.feedback = "В воздухе: 0, ракет: 0, ПВО: 172, сбито: 308, поражений: 87, разбились о препятствия: 0; удары по ПУ: пущено 0, уничтожено 0, подавлено 0, промахов 0, сбито ПВО 0; мир: 8156 секций (382 КБ), запросов 16075, неизвестно 0, лучей 87524 (ударов 1830, неизв. 1567), клеток 1318224; взрывы в блоках: 239 (подтверждено 0, без перемен 37, нет чанка 202), чужих ответов 0"
|
||||
[[step]]
|
||||
command = "airdef wave mix 10 300"
|
||||
expect.stopped = "exceeded its execution limit"
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"issue": 122,
|
||||
"kind": "diagnostic_guest_ui_path_comparison_not_exact_user_session",
|
||||
"user_report": {
|
||||
"log": "Horizon: mod airdef stopped in UI event (fuel budget 1000000000, remaining Some(0))",
|
||||
"backtrace": "airdef_core::sim::ew::Sim::known_units <- sim::raid::Sim::order_raid <- airdef::AirDef::launch_wave <- horizon_mod_sdk::Mod::ui_select <- ui-events select",
|
||||
"windows_session_confirmed_same_cause": false
|
||||
},
|
||||
"root_cause": {
|
||||
"what": "order_raid recomputed the planner snapshot known_units() for every drone (choose_target), and known_units() itself rescanned the whole force and the dedup list per intel entry",
|
||||
"growth": "drones x intel_log x force_size per UI event; the intel log holds every launch, EW disclosure, and radar disclosure of the last 6000 ticks",
|
||||
"state_at_trap": {
|
||||
"revealed_entries": 13172,
|
||||
"known_alive_units": 144,
|
||||
"sams_total": 172
|
||||
}
|
||||
},
|
||||
"fix": {
|
||||
"description": "one known_units snapshot per order_raid passed to radar_strikes and choose_target; known_units is a single O(R+S) set pass; price_grid_on reuses its snapshot",
|
||||
"semantics": "identical planner results; native equivalence test against the former predicate; WASM checkpoint /airdef stats byte-identical before/after at tick 2602",
|
||||
"limits_unchanged": true,
|
||||
"wave_size_unrestricted": true,
|
||||
"hd_record_untouched": true
|
||||
},
|
||||
"setup": {
|
||||
"defenses": "40 spread clusters x 4 heavy SAMs + 12 EW = 172, player moved per deploy step",
|
||||
"first_raid": "airdef wave mix 10 (111 drones, Smart, AllSides) via the diagnostic UI-path trigger",
|
||||
"second_raid": "same, at tick ~2600 after the first raid is fought off",
|
||||
"note": "stacked single-point layouts die to one warhead (known=0, cheap order); the trap needs a surviving force, like the user's session"
|
||||
},
|
||||
"before": {
|
||||
"source": "e6a7000 + trigger.patch (diagnostic guest only)",
|
||||
"second_order_111_drones": {
|
||||
"callback": "command (the diagnostic trigger invokes the exact ui_select(BTN_LAUNCH) handler; the UI event and the command share the same 1e9 event budget)",
|
||||
"max_used": 1000000000,
|
||||
"budget": 1000000000,
|
||||
"failures": 1,
|
||||
"mod_stopped": true,
|
||||
"log": "before-trap.txt"
|
||||
},
|
||||
"scaling_witness_11_drones_same_state": {
|
||||
"max_used": 247805865,
|
||||
"budget": 1000000000,
|
||||
"failures": 0,
|
||||
"log": "before-scaling.txt"
|
||||
}
|
||||
},
|
||||
"after": {
|
||||
"same_scenario_passed": true,
|
||||
"steps": 255,
|
||||
"max_command_fuel": 32946177,
|
||||
"max_tick_fuel": 236581288,
|
||||
"failures": 0,
|
||||
"checkpoint_stats_equal_before_after": true,
|
||||
"log": "after.txt"
|
||||
},
|
||||
"artifacts": {
|
||||
"trigger_patch": "trigger.patch",
|
||||
"scenario_before": "scenario-before.toml",
|
||||
"scenario_after": "scenario-after.toml",
|
||||
"before_guest_sha256": "6d4525cd7aaa61dbf344593f9819be1b61e06654126e9e9088e57967dc657365",
|
||||
"after_guest_sha256": "c4ef7d759524eb053ccd55f0eccdcb49c91954201eb4e0ad923ee9b2828efc4e",
|
||||
"scenario_before_sha256": "398d099275e396deab269601e5adbcf325ab5e0ea6b540c1d03a477d6b0963d1",
|
||||
"scenario_after_sha256": "3bc27338d2e6fb433f027a0cf1afc39ff9a94e1af5840454082af6d8d15aad7c"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
diff --git a/airdef-mod/src/lib.rs b/airdef-mod/src/lib.rs
|
||||
index dc6585eb3..73864e41e 100644
|
||||
--- a/airdef-mod/src/lib.rs
|
||||
+++ b/airdef-mod/src/lib.rs
|
||||
@@ -2351,8 +2351,16 @@ impl Mod for AirDef {
|
||||
.nearest_target(DVec3::new(p.x, p.y, p.z))
|
||||
.unwrap_or(DVec3::new(p.x, p.y, p.z));
|
||||
if *kind == "mix" {
|
||||
- let objects = self.objects(DVec3::new(p.x, p.y, p.z));
|
||||
- self.sim.order_raid(&wave::WaveConfig::mix(count, dist), &objects, &mut out);
|
||||
+ // DIAGNOSTIC ONLY (issue #122): drive the exact UI launch path
|
||||
+ // (Page::WaveGo, BTN_LAUNCH) with the maximum menu composition.
|
||||
+ let mut wave = wave::WaveConfig::default();
|
||||
+ wave.counts = [40; wave::KINDS.len()];
|
||||
+ wave.dist = dist;
|
||||
+ wave.approach = sim::Approach::AllSides;
|
||||
+ wave.aim = sim::Aim::Smart;
|
||||
+ self.wave = wave;
|
||||
+ self.menu = Some(Page::WaveGo);
|
||||
+ self.ui_select(BTN_LAUNCH, 0);
|
||||
} else {
|
||||
let kind = DroneKind::from_key(kind).ok_or(
|
||||
"Тип: shahed, geran3, lancet, gerbera, kh101, kalibr, kh22, zircon, iskander, kinzhal, oreshnik или mix",
|
||||
+4
-2
@@ -158,7 +158,7 @@ def test_wasm():
|
||||
|
||||
|
||||
BUDGET_SCENARIO = "tests/budgets/wasm_flat_budget.toml"
|
||||
BUDGET_ASSERTED_SHA256 = "bcc0359289ab20ab3de608bcc130f72d2cf88f9f6fbc470d7b6846e9db5d11df"
|
||||
BUDGET_ASSERTED_SHA256 = "134cb4667691cf9faa639ed37120941d829cfc13ce45e9638582c409d9e0dd29"
|
||||
|
||||
|
||||
def fuel_measurements(output):
|
||||
@@ -280,8 +280,10 @@ def test_budgets():
|
||||
report["status"] = "failed"
|
||||
raise RuntimeError("pinned runtime did not return requested callback fuel counters")
|
||||
# These assertions belong to this exact fixture, not to arbitrary edited scripts.
|
||||
# Two `airdef wave oreshnik` orders plus two Smart orders through the UI launch
|
||||
# path (`airdef preset` + `airdef raid`, the wave screen's ПУСК handler, #122).
|
||||
if result.returncode == 0 and report["scenario_sha256"] == BUDGET_ASSERTED_SHA256:
|
||||
counts["accepted_raids_asserted"] = 2
|
||||
counts["accepted_raids_asserted"] = 4
|
||||
counts["snapshot_airborne_asserted"] = 184
|
||||
counts["snapshot_tick"] = 2102
|
||||
except Exception as exc:
|
||||
|
||||
@@ -123,7 +123,7 @@ class BudgetReportFailureTests(unittest.TestCase):
|
||||
self.assertEqual(report["scenario"], airdef.BUDGET_SCENARIO)
|
||||
self.assertEqual(report["scenario_render_world"], "flat at y=64")
|
||||
counts = report["scenario_counts"]
|
||||
self.assertEqual((counts["accepted_raids_asserted"], counts["snapshot_airborne_asserted"], counts["snapshot_tick"]), (2, 184, 2102))
|
||||
self.assertEqual((counts["accepted_raids_asserted"], counts["snapshot_airborne_asserted"], counts["snapshot_tick"]), (4, 184, 2102))
|
||||
self.assertIsNone(counts["peak_airborne_observed"])
|
||||
self.assertEqual(report["measured_fuel"]["callbacks"], [{
|
||||
"mod": "airdef", "callback": "tick", "calls": 2102,
|
||||
|
||||
Reference in New Issue
Block a user