///|
/// two_peer:最小可运行的联机闭环。
///
/// 两个对等体各跑一个 `Session`,包走真实字节协议(`encode_batch`)和
/// 一条 3 帧延迟 + 1 帧抖动的确定性链路。每帧每端都把自己的最近 8 帧输入
/// 连同一个确认帧、一个校验和帧发出去——这就是「伪可靠层」的全部内容。
///
/// moon run examples/two_peer
const RESEND : Int32 = 8
///|
const TOTAL : Int32 = 600
///|
const DELAY : Int32 = 3
///|
const JITTER : Int32 = 1
///|
/// 取「两端都已确认」的最高帧,比它上面的历史哈希。
fn synced(
pa : @session.Session[@scene.Scene],
pb : @session.Session[@scene.Scene],
) -> Bool {
let a = pa.confirmed()
let b = pb.confirmed()
let c = if a < b { a } else { b }
match (pa.hash_at(c), pb.hash_at(c)) {
(Some(x), Some(y)) => x == y
_ => true
}
}
///|
fn main {
let pa = @session.Session::with_window(@scene.Scene::new(), 0, 128)
let pb = @session.Session::with_window(@scene.Scene::new(), 1, 128)
let net_ab = @sim_net.NetSim::new(
@sim_net.Link::new(DELAY, JITTER, 0, 0, 20261028UL),
)
let net_ba = @sim_net.NetSim::new(
@sim_net.Link::new(DELAY, JITTER, 0, 0, 777UL),
)
println(
"two_peer:两个 Session + 真实字节协议 + \{DELAY} 帧延迟 + \{JITTER} 帧抖动",
)
println("")
println("frame p0.confirm p1.confirm rollbacks synced")
let mut mismatches = 0
let mut f = 0
while f < TOTAL {
f = f + 1
ignore(pa.advance(@scene.scripted_input(f, 0)))
ignore(pb.advance(@scene.scripted_input(f, 1)))
let mut back = f - RESEND + 1
if back < 1 {
back = 1
}
// 每端把 [最近 RESEND 帧输入] + [确认帧] + [校验和帧] 打成一个包。
let ab : Array[@protocol.Message] = []
let ba : Array[@protocol.Message] = []
let mut m = back
while m <= f {
ab.push(
@protocol.Message::Input(
@protocol.InputFrame::new_from(0, m, [@scene.scripted_input(m, 0)]),
),
)
ba.push(
@protocol.Message::Input(
@protocol.InputFrame::new_from(1, m, [@scene.scripted_input(m, 1)]),
),
)
m = m + 1
}
ab.push(
@protocol.Message::Confirm(@protocol.ConfirmFrame::new(pa.confirmed())),
)
ba.push(
@protocol.Message::Confirm(@protocol.ConfirmFrame::new(pb.confirmed())),
)
match pa.hash_at(pa.confirmed()) {
Some(h) =>
ab.push(
@protocol.Message::Checksum(
@protocol.ChecksumFrame::new(pa.confirmed(), h),
),
)
None => ()
}
match pb.hash_at(pb.confirmed()) {
Some(h) =>
ba.push(
@protocol.Message::Checksum(
@protocol.ChecksumFrame::new(pb.confirmed(), h),
),
)
None => ()
}
net_ab.send(f, @protocol.encode_batch(ab))
net_ba.send(f, @protocol.encode_batch(ba))
for payload in net_ab.recv(f) {
mismatches = mismatches + @scene.apply(payload, pb)
}
for payload in net_ba.recv(f) {
mismatches = mismatches + @scene.apply(payload, pa)
}
if f % 60 == 0 {
let ok = synced(pa, pb)
let status = if ok { "yes" } else { "NO" }
println(
"\{pad(f.to_string(), 7)}\{pad(pa.confirmed().to_string(), 12)}\{pad(pb.confirmed().to_string(), 12)}\{pad(pa.rollbacks().to_string() + "/" + pb.rollbacks().to_string(), 11)}\{status}",
)
}
}
// 收尾:最后一轮包还在路上,多跑几帧让确认追上。
let mut t = TOTAL
while t < TOTAL + 64 {
t = t + 1
for payload in net_ab.recv(t) {
mismatches = mismatches + @scene.apply(payload, pb)
}
for payload in net_ba.recv(t) {
mismatches = mismatches + @scene.apply(payload, pa)
}
}
println("")
println("终态:一致 = \{synced(pa, pb)},校验失配 = \{mismatches}")
println(
" 回滚 = \{pa.rollbacks()}/\{pb.rollbacks()},确认帧 = \{pa.confirmed()}/\{pb.confirmed()}",
)
println(
" 丢包 = \{net_ab.dropped()}/\{net_ba.dropped()},在途 = \{net_ab.in_flight()}/\{net_ba.in_flight()}",
)
}
///|
/// 右侧补空格,让表格在等宽字体里对齐。
fn pad(s : String, width : Int32) -> String {
let mut out = s
while out.length() < width {
out = out + " "
}
out
}