跳转到内容

运行军事扩展:从接敌到占领

本教程运行军事参考起步示例。它建立一支野战部队,补充兵员,向有守军的领地行军,结算战斗并占领该地,最后确认保存和重放得到的副本与原运行一致。你会了解 canwu-military 如何接收命令、随机性从哪里来,以及哪些状态交给其他扩展管理。

军事模拟扩展分为三个 crate:

  • canwu-military:军事领域记录、MilitaryCommand 领域命令、边界系统、抽样证据和发给指挥官的报告,构建在 canwu-api 之上。
  • canwu-military-reference-content:两套带版本的规则集。
  • canwu-military-reference:可替换的参考整合包,为参考世界加入军事目录。

在仓库根目录运行:

Terminal window
cargo run -p canwu-military-reference --example military_starter
military_gameplan=complete riverine=riverine_preindustrial industrial=industrial_front checkpoint=3b647676eb58a48594665edc09d190b25eb7932e4d630496c3f57fc6ddc2b691

military_gameplan=complete 表示所有断言都已通过:战斗得出了结果,占领已经建立,恢复和重放得到的副本与原运行的检查点哈希相同。riverine 和 industrial 打印的是两套参考规则集的名称。

军事命令经规范化输入准入,在第 7 阶段应用,在第 13 阶段向指挥官报告. 查看图表源码.
查看图表源码
flowchart TB
  Cmd["military_command()<br/>Command::Plugin"] --> Queue["enqueue_command()<br/>规范化输入"]
  Queue --> Admit["准入检查<br/>指挥官、版本、<br/>兵种、战术"]
  Admit --> Packet["military_command_v1<br/>输入数据包"]
  Packet --> Apply["第 7 阶段<br/>写入记录、抽样"]
  Apply --> Report["第 13 阶段<br/>向指挥官报告"]

起步示例通过同一个辅助函数发送所有命令:

fn submit(
canwu: &mut Canwu,
sequence: u64,
issuer: Issuer,
command: MilitaryCommand,
) -> Result<(), Box<dyn std::error::Error>> {
let envelope = CommandEnvelope::new(issuer, military_command(command)?);
canwu.enqueue_command(
canwu.time(),
i32::try_from(sequence)?,
CommandRequest::new(CommandRequestId::new(sequence), canwu.revision(), envelope),
)?;
canwu.advance_canonical(SimDuration::minutes(1))?;
Ok(())
}

军事命令必须走规范化输入(enqueue_command);直接调用 submit 会返回 MixedCommandIngress。准入时,处理器检查签发者是否指挥这支部队、expected_force_revision 是否与部队记录一致,以及兵种或战术是否存在于当前规则集中。检查通过后,它排入一个 military_command_v1 数据包,由第 7 阶段的系统 apply-military-ingress-v1 应用。

每条命令都带一个 MilitaryOperationKey。同一个键配上不同输入,会以 IdempotencyConflict 失败。用同一个键、同样的输入再次发送,只有在这次发送通过准入时才是空操作。带 expected_force_revision 的命令在第一次执行提高了部队修订号之后,再发一次就会在准入时因版本过期(DomainRecordVersionConflict)而失败,这会让边界失败,并把该输入留在队列里。

demo_military_scenario() 以参考世界的演示场景为基础,加入使用 riverine_preindustrial 规则集的军事目录。示例注册 ReferenceWorldPlugin 和 military_plugin(),然后发送五条命令:

步骤 命令 结果
1 CreateForce 沈将军的野战部队驻在中部节点,编制 2,500 人,实有 2,000 人。
2 Recruit 新增 400 人的 reserve 子单位,部队记录升到版本 2。
3 PlanOperation 一条处于 Planned 阶段的战略行动记录。
4 CreateForce 东部节点上一支 100 人的守军,由罗大臣(Minister Luo)指挥。
5 OrderMarch 野战部队向东部节点行军,以守军为对手。

起步示例中的行军命令如下:

MilitaryCommand::OrderMarch {
operation: canwu_military::MilitaryOperationKey::new("canwu.military:op:march-front")?,
force: force.clone(),
operation_id: OperationId::new("canwu.military:operation:march-front")?,
destination: MilitaryNodeId::new(format!(
"canwu.military:node:{}",
ids.eastern_territory
))?,
objective: "secure the eastern route".to_owned(),
tactic: "crossing_assault".to_owned(),
opposing_force: Some(ForceId::new("canwu.military:force:defender")?),
expected_force_revision: 2,
}

随后 canwu.advance_canonical(SimDuration::days(5)) 推进五天:

  1. 下令一分钟后,部队抵达目的地,接敌产生一条战斗记录。参考插件把行军当作一分钟的一步,并记录路线摘要(部队和目的地的哈希),详见组合移动。
  2. 交战中的行动每天结算一个战斗轮,每轮从 military_random_stream() 抽样一次,并记录为抽样证据。本次运行中,2,400 人的进攻方损失 7 人,在第一轮就全歼 100 人的守军,战斗以 AttackerVictory 结束。
  3. 胜利后,目的地建立占领,阶段为 MilitaryControl。占领每天更新一次,安全度和行政覆盖逐步上升;运行结束时分别为 600 和 80(千分比)。

MilitaryCommand 有 13 个变体(含内部使用的 AdvanceTick)。canwu-military 只写入自己的部队、行动、战斗、占领、账本和提供方结果记录;路线、资源、人口、法律和财政程序留给各自的扩展。每条命令检查和写入什么、战斗与占领规则,以及特种行动的已知问题,见军事系统。

canwu-military-reference-content 提供 riverine_preindustrial 和 industrial_front 两套规则集,都标记为 synthetic_reference:它们用于覆盖不同的兵种、战术、地形、征募、战斗和占领假设,不代表任何历史结论。当前插件读取生效的规则集,用来校验兵种和战术名称;战斗和占领的计算写在 canwu-military 内部。

当前的 canwu-military 插件在下令一分钟后完成行军,只记录路线摘要。需要路线和行军耗时的上层应用,应在自己的整合中围绕行军组合 canwu-routing 与 canwu-transport 或 canwu-movement,参见路线规划与送信执行。

完整的命令序列以及快照和重放检查,见起步示例源码。