阶段边界与资源分配
本示例中,供给插件提供 10 单位粮食,需求插件在同一个每日边界中申请 6 单位。参伍完成分配后,同一边界的后续阶段记录守军实际得到的粮食。你会了解边界阶段如何为不同插件的工作排序,以及系统如何读取已结算的分配结果。
边界是一次原子结算:参伍按 14 个固定阶段运行已注册的系统,然后要么提交全部变化,要么全部回滚。各阶段见结算系统。
打开 phased_boundary.rs
浏览示例目录
在仓库根目录运行:
cargo run -p canwu-api --example phased_boundary示例没有任何输出。两条断言都通过时程序以状态码 0 退出:需求得到 6 单位粮食,边界产生两个事件。
插件如何协作
Section titled “插件如何协作”查看图表源码
flowchart LR
Offer["example-supply / offer<br/>第 6 阶段:提供 10 单位粮食"] --> Allocate["参伍分配<br/>第 6 阶段末"]
Request["example-demand / request<br/>第 6 阶段:申请 6 单位粮食"] --> Allocate
Allocate --> Apply["example-demand / apply<br/>第 7 阶段:读取分配结果并写入组件"]
Apply --> Commit["校验并提交<br/>第 8、9 阶段"]
1. 在第 6 阶段提供供给
Section titled “1. 在第 6 阶段提供供给”SupplyPlugin 在 BoundaryPhase::ReservationAndAllocation(第 6 阶段)注册一个名为 offer、按日执行的边界系统:
let mut contract = BoundarySystemContract::new( "offer", BoundaryPhase::ReservationAndAllocation, SystemCadence::Daily,);contract.reservation_offers = vec![StateKey::new("logistics", "grain")];registrar.register_boundary_system(contract, offer_grain)offer_grain 为 grain_pool() 返回一个 10 单位的 ReservationOffer。这个池是领地 1 上、挂在 logistics/grain 状态键下的 grain 资源。契约的 reservation_offers 必须列出这个状态键。
2. 在同一阶段申请粮食
Section titled “2. 在同一阶段申请粮食”DemandPlugin 在同一阶段注册 request,request_grain 返回:
ReservationRequest { request: "daily-grain".to_owned(), pool: grain_pool(), quantity: 6, priority: 10, tie_break: "western-garrison".to_owned(),}第 6 阶段结束时,参伍逐个资源池进行分配:priority 高的申请先分;优先级相同时按 tie_break 排序,再按申请标识排序。每个申请最多拿到它的 quantity,池中剩多少就给多少。插件的注册顺序不影响分配结果。
3. 在第 7 阶段读取分配结果
Section titled “3. 在第 7 阶段读取分配结果”DemandPlugin 还在 BoundaryPhase::DomainDeltaProposal(第 7 阶段)注册了 apply。它的契约声明要读取的预留、要写入的状态和要产生的事件:
apply.writes = vec![StateKey::new("garrison", "grain")];apply.emits = vec!["grain_allocated".to_owned()];apply.reservation_reads = vec![ReservationRef::new( "example-demand", "request", "daily-grain",)];apply_grain 通过 view.reservation(&reservation) 读取结算结果,返回两条指令:SetComponent 把 allocation.granted 存到领地 1 上,Emit 记录一个 grain_allocated 事件。
4. 结算边界
Section titled “4. 结算边界”let mut canwu = Canwu::demo(35)?;canwu.register_plugin(&SupplyPlugin)?;canwu.register_plugin(&DemandPlugin)?;
let receipt = canwu .settle_boundary(BoundaryRequest::at(canwu.time()).with_cadence(SystemCadence::Daily))?;assert_eq!(receipt.allocations[0].granted, 6);assert_eq!(canwu.boundaries()[0].emissions.len(), 2);settle_boundary 运行执行频率与请求相符的所有系统。回执和保存下来的 BoundaryRecord 都记录了供给、申请和分配结果,作为证据。两个事件分别是 SetComponent 产生的 daily_grant_changed 和 Emit 产生的 grain_allocated。
什么时候使用边界
Section titled “什么时候使用边界”多个系统需要在同一模拟时间按固定顺序协作时,例如日结或回合结算,就使用边界系统。单个玩家动作用命令即可,见命令插件。
把第一条断言换成下面的循环,打印每项分配结果和事件日志:
for allocation in &receipt.allocations { println!( "requested={} granted={} remaining={} {:?}", allocation.requested, allocation.granted, allocation.remaining_after, allocation.disposition );}for event in canwu.events() { println!("{} {}", event.kind.qualified_event_type(), event.summary);}requested=6 granted=6 remaining=4 Fulfilledexample-demand.daily_grant_changed Granted 6 grain to the western garrisonexample-demand.grain_allocated The daily grain allocation settled再把 request_grain 中的 quantity: 6 改为 quantity: 12。池中只有 10 单位,申请只能部分满足:
requested=12 granted=10 remaining=0 Partialexample-demand.daily_grant_changed Granted 10 grain to the western garrisonexample-demand.grain_allocated The daily grain allocation settled