ludic/packages/ludic.net/receive.ludic
Orkuncakilkaya ca69a3f9ae ludic.net: a message's record, bytes and lists kept - queued ones back to a pool once sent or taken, the inbox's two drains on, a read text interned
np_queue made a NetMsg and a buffer per message, every flush a new keep list, and every message
that came in a NetMessage and a buffer; nr_text a new string per text read. A party plays at
dozens a second, so all of it is kept now: a pool of MTU-sized records for the queue (a peer's two
lists swapped by the flush), the inbox's records in two halves swapped when a message finds the
inbox empty, net_written's one record, a relay hello's and a STUN request's bytes in one scratch
buffer, and the texts read out interned. Tests: 6000 messages with the heap flat (Os.heap_bytes),
and an inbox record that holds still across a drain and comes back two drains on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:25:46 +03:00

90 lines
3.5 KiB
Text

# receive.ludic - what arrived, unwrapped from the relay, checked, acknowledged and put in order
# on the inbox. A reliable message is taken strictly in order; one that arrives early is dropped
# and comes again.
function net__pump(net_st: mut NetState) -> void {
if net_st.net__sock <= 0 { return }
for guard in 0 .. 200 {
let n = NetSocket.recv(net_st.net__sock, net_st.net__in, NET_MTU + 64)
if n <= 0 { return }
let n2 = net__unwrap(net_st, n, NetSocket.from_ip(net_st.net__sock), NetSocket.from_port(net_st.net__sock))
if n2 > 0 { net__packet(net_st, n2, net_st.net__from_ip, net_st.net__from_port) }
}
}
function net__packet(net_st: mut NetState, n: int, ip: int, port: int) -> void {
if NetWorld.trace() { net__log(`in {n} bytes from {Udp.ip_text(ip)}:{port}`) }
if n < 20 { return }
if net__stun_packet(net_st, n) { return } # what the router looks like from outside
if nb_get8(net_st.net__in, 0) != 'M' or nb_get8(net_st.net__in, 1) != 'L' { return } # a punch ("MP") or noise
if nb_get8(net_st.net__in, 2) != net__conf(net_st).proto { return }
let slot = net__slot_for(net_st, n, ip, port)
if slot < 0 { return }
let p = net_st.net__peers[slot]
p.heard = net_st.net__time
# only what this connection has sent can be acknowledged: a packet from the one before it
# (a re-join) carries the old numbers and would drop the new hello as already taken
let ack = nb_get32(net_st.net__in, 12)
if ack > p.acked and ack < p.next_id { p.acked = ack }
net__messages(net_st, slot, n)
}
# the known peer at that address, a stranger's new slot (a host, and only with a hello), or -1
function net__slot_for(net_st: mut NetState, n: int, ip: int, port: int) -> int {
let token = nb_get32(net_st.net__in, 4)
var slot = net__find(net_st, ip, port)
if slot >= 0 {
if token != net_st.net__token and net_st.net__state != NET_JOINING and net_st.net__peers[slot].pid != 0 { return -1 }
return slot
}
if net_st.net__state != NET_HOSTING { return -1 }
if n < 28 or nb_get8(net_st.net__in, 20) != NET_HELLO { return -1 }
for i in 1 .. NET_PEERS {
if not net_st.net__peers[i].on {
net__open(net_st, i, ip, port, 0)
return i
}
}
return -1
}
function net__messages(net_st: mut NetState, slot: int, n: int) -> void {
let p = net_st.net__peers[slot]
let from = nb_get32(net_st.net__in, 8)
let count = nb_get32(net_st.net__in, 16)
var o = 20
for c in 0 .. count {
if o + 4 > n { return }
let kind = nb_get8(net_st.net__in, o)
let rel = nb_get8(net_st.net__in, o + 1) == 1
o += 2
var id = 0
if rel {
if o + 4 > n { return }
id = nb_get32(net_st.net__in, o)
o += 4
}
let ml = nb_get8(net_st.net__in, o) | (nb_get8(net_st.net__in, o + 1) << 8)
o += 2
if ml < 0 or o + ml > n { return }
var take = true
if rel {
p.ack_due = true
if id != p.recv_id + 1 { take = false } else { p.recv_id = id }
}
if take { net__deliver(net_st, slot, from, kind, o, ml) }
o += ml
}
}
# onto the inbox - except a heartbeat, and on a host anything but a hello before its welcome
function net__deliver(net_st: mut NetState, slot: int, from: int, kind: int, o: int, ml: int) -> void {
if kind == NET_BEAT { return }
if net_st.net__state == NET_HOSTING and net_st.net__peers[slot].pid < 2 and kind != NET_HELLO { return }
let m = net__in_take(net_st)
m.slot = slot
m.from = from
m.kind = kind
m.len = ml
nb_copy(m.data, 0, net_st.net__in, o, ml)
q_push(net_inbox(net_st), m)
}