# tick.ludic - a frame of the session, in three calls with the game's between them: net_begin # (time, and what came in onto the inbox), net_update (the matchmaker, the hello, silence) and, # once the game has queued its own, net_end (heartbeats, sending, letting slots go) export function net_begin(net_st: mut NetState, dt: float) -> void { if not net_active(net_st) { return } net_st.net__time = net_st.net__time + dt net__pump(net_st) } export function net_update(net_st: mut NetState, dt: float) -> void { if not net_active(net_st) { return } net__room_tick(net_st, dt) if net_st.net__state == NET_JOINING { net__hello_tick(net_st, dt) return } for i in 0 .. NET_PEERS { let p = net_st.net__peers[i] if not p.on or net_st.net__time - p.heard <= float(NET_TIMEOUT) { continue } if net_st.net__state == NET_JOINED { net_lose(net_st, NET_WHY_HOST_SILENT) return } let gone = p.pid np_close(net_st, i) net__fact(net_st, NET_GONE, i, gone, 0, "") } } function net__hello_tick(net_st: mut NetState, dt: float) -> void { if net_st.net__hello_t < 0.0 { nb_copy(net_st.net__wb, 0, net_st.net__hello_b, 0, net_st.net__hello_l) net_st.net__wl = net_st.net__hello_l np_queue(net_st, 0, NET_HELLO, true) net_st.net__hello_t = 0.0 } net_st.net__hello_t = net_st.net__hello_t + dt if net_st.net__hello_t > float(NET_TIMEOUT + net_st.net__wait_more) { net_lose(net_st, NET_WHY_NO_ANSWER) return } np_flush(net_st, 0) } export function net_end(net_st: mut NetState) -> void { if not net_live(net_st) { return } let beat = net_st.net__time - net_st.net__beat_t >= 1.0 if beat { net_st.net__beat_t = net_st.net__time } for i in 0 .. NET_PEERS { let p = net_st.net__peers[i] if not p.on { continue } if beat { nw_begin(net_st) np_queue(net_st, i, NET_BEAT, false) } np_flush(net_st, i) if p.closing and (p.acked >= p.close_id or net_st.net__time > p.close_t) { let pid = p.pid np_close(net_st, i) net__fact(net_st, NET_CLOSED, i, pid, 0, "") } } } # while a load holds the frame: keep answering, so nobody times out on this machine export function net_keepalive(net_st: mut NetState) -> void { if not net_live(net_st) { return } net__pump(net_st) for i in 0 .. NET_PEERS { let p = net_st.net__peers[i] if p.on { p.heard = net_st.net__time nw_begin(net_st) np_queue(net_st, i, NET_BEAT, false) np_flush(net_st, i) } } } # every queue to its peer now, three times over (a machine about to let go) export function net_flush_all(net_st: mut NetState) -> void { for t in 0 .. 3 { for i in 0 .. NET_PEERS { np_flush(net_st, i) } } } # the session is over for this machine: every slot, the socket and the matchmaker let go export function net_reset(net_st: mut NetState) -> void { if net_st.net__peers != null { for i in 0 .. NET_PEERS { np_close(net_st, i) } } if net_st.net__sock > 0 { NetSocket.close(net_st.net__sock) net_st.net__sock = 0 } net_st.net__state = NET_IDLE net_st.net__my_pid = 0 net_st.net__wait_more = 0 net__room_reset(net_st) } # out of the session, and why: the game hears NET_DOWN and says so in its own words export function net_lose(net_st: mut NetState, why: int) -> void { net__lose_with(net_st, why, "") } # ... with what the reason is about (the room code nobody had) @alloc_ok("once, when the party is lost") function net__lose_with(net_st: mut NetState, why: int, text: string) -> void { net_reset(net_st) net_st.net__state = NET_LOST net_st.net__why = why net__log(`lost ({why})`) net__fact(net_st, NET_DOWN, -1, 0, why, text) } # a lost session read and done with: idle again export function net_fresh(net_st: mut NetState) -> void { if net_st.net__state == NET_LOST { net_st.net__state = NET_IDLE } }