ludic/packages/ludic.net/relay.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

84 lines
3.5 KiB
Text

# relay.ludic - a router that maps every destination to a new port defeats punching, so a guest
# the host does not answer goes through the party server: "MLRG" + room + packet out, "MLRS" +
# session + packet back. The host keeps its address there fresh ("MLRH" + room) and sees each
# relayed guest as the relay's address with a port of relay_port0 + its session.
function net__take_relay(net_st: mut NetState, v: Val) -> void {
let r = net__json_str(v, "relay")
if len(r) > 0 and net_parse_code(net_st, r) {
net_st.net__relay_ip = net_st.net__code_ip
net_st.net__relay_port = net_st.net__code_port
}
}
function net__code_bytes(net_st: NetState, b: []byte, o: int) -> void {
let cp = net_st.net__room
for k in 0 .. 6 {
var c = 0
if k < len(cp) { c = cp[k] }
nb_put8(b, o + k, c)
}
}
function net__relay_hello(net_st: NetState) -> void {
let b = net_st.net__sb
nb_put8(b, 0, 'M')
nb_put8(b, 1, 'L')
nb_put8(b, 2, 'R')
nb_put8(b, 3, 'H')
net__code_bytes(net_st, b, 4)
NetSocket.send(net_st.net__sock, net_st.net__relay_ip, net_st.net__relay_port, b, 10)
}
function net__use_relay(net_st: mut NetState) -> void {
net_st.net__via_relay = true
net__log(`room {net_st.net__room}: no answer directly, through the relay`)
net_dial(net_st, net_st.net__relay_ip, net_st.net__relay_port)
}
export function net_via_relay(net_st: NetState) -> bool { return net_st.net__via_relay }
function net__relayed(net_st: NetState, p: NetPeer) -> bool {
return net_st.net__relay_ip != 0 and p.ip == net_st.net__relay_ip and (p.port >= net__conf(net_st).relay_port0 or net_st.net__via_relay)
}
# a packet of `o` bytes in net__out, to peer p: straight, or wrapped for the relay
function net__relay_send(net_st: mut NetState, p: NetPeer, o: int) -> int {
if not net__relayed(net_st, p) { return NetSocket.send(net_st.net__sock, p.ip, p.port, net_st.net__out, o) }
var hdr = 10
if not net_st.net__via_relay { hdr = 6 }
var k = o - 1
while k >= 0 {
net_st.net__out[k + hdr] = net_st.net__out[k]
k -= 1
}
nb_put8(net_st.net__out, 0, 'M')
nb_put8(net_st.net__out, 1, 'L')
nb_put8(net_st.net__out, 2, 'R')
if net_st.net__via_relay {
nb_put8(net_st.net__out, 3, 'G')
net__code_bytes(net_st, net_st.net__out, 4)
} else {
let sid = p.port - net__conf(net_st).relay_port0
nb_put8(net_st.net__out, 3, 'S')
nb_put8(net_st.net__out, 4, (sid >> 8) & 255)
nb_put8(net_st.net__out, 5, sid & 255)
}
let sent = NetSocket.send(net_st.net__sock, net_st.net__relay_ip, net_st.net__relay_port, net_st.net__out, o + hdr)
nb_copy(net_st.net__out, 0, net_st.net__out, hdr, o) # the packet back where it was
return sent
}
# a relayed packet is unwrapped in place and given the sender it stands for; the game packet's
# length comes back, 0 when there is nothing for the game
function net__unwrap(net_st: mut NetState, n: int, ip: int, port: int) -> int {
net_st.net__from_ip = ip
net_st.net__from_port = port
if n < 6 or net_st.net__relay_ip == 0 or ip != net_st.net__relay_ip or port != net_st.net__relay_port { return n }
if nb_get8(net_st.net__in, 0) != 'M' or nb_get8(net_st.net__in, 1) != 'L' or nb_get8(net_st.net__in, 2) != 'R' or nb_get8(net_st.net__in, 3) != 'S' { return n }
let sid = (nb_get8(net_st.net__in, 4) << 8) | nb_get8(net_st.net__in, 5)
nb_copy(net_st.net__in, 0, net_st.net__in, 6, n - 6)
# the host: this guest's session; a guest: the relay itself is its host
if sid > 0 { net_st.net__from_port = net__conf(net_st).relay_port0 + sid }
return n - 6
}