# send.ludic - everything due goes out now, in as many packets as it takes (up to eight a call). # A packet is a 20-byte header - "ML", the protocol, 1, the session token, the sender's pid, the # highest reliable id taken in order, a message count - then each message: kind, flags, a # reliable id when it is reliable, a 16-bit length, the payload. A reliable message goes again # every quarter second until acknowledged; an unreliable one half a second old is dropped. export function np_flush(net_st: mut NetState, slot: int) -> void { if not net_peer_on(net_st, slot) { return } let p = net_st.net__peers[slot] for pk in 0 .. 8 { if not net__flush_one(net_st, p) { return } } } function net__flush_one(net_st: mut NetState, p: NetPeer) -> bool { var o = 20 var count = 0 let keep = p.back List.clear(keep) for i in 0 .. len(p.queue) { let m = p.queue[i] let taken = m.rel and m.id <= p.acked let stale = not m.rel and net_st.net__time - m.made > 0.5 if taken or stale { kept_push(net_st.net__free, m) # back to the pool continue } var due = true if m.rel and not (m.sent < 0.0) and net_st.net__time - m.sent < 0.25 { due = false } var size = 4 + m.len if m.rel { size += 4 } if due and o + size <= NET_MTU and count < 250 { o = net__put_msg(net_st, o, m) count += 1 m.sent = net_st.net__time if net_st.net__kind_out != null and m.kind < 64 { net_st.net__kind_out[m.kind] += size } if m.rel { kept_push(keep, m) } else { kept_push(net_st.net__free, m) } # a reliable one kept until acknowledged } else { kept_push(keep, m) # not due, or waits for the next packet } } p.back = p.queue p.queue = keep if count == 0 and not p.ack_due { return false } nb_put8(net_st.net__out, 0, 'M') nb_put8(net_st.net__out, 1, 'L') nb_put8(net_st.net__out, 2, net__conf(net_st).proto) nb_put8(net_st.net__out, 3, 1) nb_put32(net_st.net__out, 4, net_st.net__token) nb_put32(net_st.net__out, 8, net_st.net__my_pid) nb_put32(net_st.net__out, 12, p.recv_id) nb_put32(net_st.net__out, 16, count) let sent = net__relay_send(net_st, p, o) if sent > 0 { net_st.net__bytes_out += sent + 28 } # with the IP and UDP headers: what the line carries @alloc_ok("only while tracing every packet") if NetWorld.trace() { net__log(`out {o} bytes to {Udp.ip_text(p.ip)}:{p.port}: {sent}`) } p.ack_due = false return count > 0 } function net__put_msg(net_st: NetState, at: int, m: NetMsg) -> int { var o = at nb_put8(net_st.net__out, o, m.kind) nb_put8(net_st.net__out, o + 1, 0) if m.rel { nb_put8(net_st.net__out, o + 1, 1) } o += 2 if m.rel { nb_put32(net_st.net__out, o, m.id) o += 4 } nb_put8(net_st.net__out, o, m.len & 255) nb_put8(net_st.net__out, o + 1, (m.len >> 8) & 255) o += 2 nb_copy(net_st.net__out, o, m.data, 0, m.len) return o + m.len } # the per-kind byte count starts again (a game measuring what a party costs its upload) export function net_kind_out_clear(net_st: mut NetState) -> void { if net_st.net__kind_out == null { @alloc_ok("once: the counts are made the first time they are cleared") net_st.net__kind_out = words(64) } for k in 0 .. 64 { net_st.net__kind_out[k] = 0 } } export function net_kind_bytes(net_st: NetState, kind: int) -> int { if net_st.net__kind_out == null or kind < 0 or kind >= 64 { return 0 } return net_st.net__kind_out[kind] } export function net_bytes_out(net_st: NetState) -> int { return net_st.net__bytes_out }