# 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 = buffer(10) 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: mut 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 }