wip(0.S2): migrate - a var nothing writes becomes a let; a state is keyed by its module, directory or program, so every program's plan agrees; paths normalized; program states named SceneDemoState / scene_demo_st; the LSP reads state, mut and entry/handler parameters

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 15:31:34 +03:00
parent 3eda72e8c2
commit 5ffe50ed02
463 changed files with 72045 additions and 67421 deletions

View file

@ -5,52 +5,50 @@ import "ludic.base"
program NetTest {
numbers float
property Pkt { ip: int = 0, port: int = 0, len: int = 0, data: []byte }
state NettestState {
sent: []Pkt = null
inbound: []Pkt = null
from_ip: int = 0
from_port: int = 0
}
var sent: []Pkt = null
var inbound: []Pkt = null
var from_ip: int = 0
var from_port: int = 0
function fake_open(port: int) -> int { return 5 }
function fake_close(h: int) -> void { }
function fake_port(h: int) -> int { return 40000 }
function fake_local() -> int { return Udp.ip("10.0.0.5") }
function fake_from_ip(nettest_st: NettestState, h: int) -> int { return nettest_st.from_ip }
function fake_from_port(nettest_st: NettestState, h: int) -> int { return nettest_st.from_port }
function fake_send(nettest_st: mut NettestState, h: int, ip: int, port: int, b: []byte, n: int) -> int {
function fake_from_ip(h: int) -> int { return from_ip }
function fake_from_port(h: int) -> int { return from_port }
function fake_send(h: int, ip: int, port: int, b: []byte, n: int) -> int {
let p = new Pkt
p.ip = ip
p.port = port
p.len = n
p.data = buffer(n)
nb_copy(p.data, 0, b, 0, n)
push(nettest_st.sent, p)
push(sent, p)
return n
}
function fake_recv(nettest_st: mut NettestState, h: int, b: []byte, cap: int) -> int {
if len(nettest_st.inbound) == 0 { return 0 }
let p = nettest_st.inbound[0]
function fake_recv(h: int, b: []byte, cap: int) -> int {
if len(inbound) == 0 { return 0 }
let p = inbound[0]
let rest = new []Pkt
for i in 1 .. len(nettest_st.inbound) { push(rest, nettest_st.inbound[i]) }
nettest_st.inbound = rest
for i in 1 .. len(inbound) { push(rest, inbound[i]) }
inbound = rest
nb_copy(b, 0, p.data, 0, p.len)
nettest_st.from_ip = p.ip
nettest_st.from_port = p.port
from_ip = p.ip
from_port = p.port
return p.len
}
bind NetSocket { open: fn fake_open, close: fn fake_close, send: fn fake_send, recv: fn fake_recv, from_ip: fn fake_from_ip, from_port: fn fake_from_port, port: fn fake_port, local_ip: fn fake_local }
function fresh(base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) -> void {
nettest_st.sent = new []Pkt
nettest_st.inbound = new []Pkt
net_reset(net_st)
q_clear(base_st, net_inbox(base_st, net_st))
q_clear(base_st, net_facts(base_st, net_st))
function fresh() -> void {
sent = new []Pkt
inbound = new []Pkt
net_reset()
q_clear(net_inbox())
q_clear(net_facts())
}
# a packet from ip:port: token, pid, ack, then one message of `kind` (reliable with `id` > 0)
function arrive(nettest_st: mut NettestState, ip: int, port: int, token: int, pid: int, ack: int, kind: int, id: int, payload: int) -> void {
function arrive(ip: int, port: int, token: int, pid: int, ack: int, kind: int, id: int, payload: int) -> void {
let b = buffer(40)
nb_put8(b, 0, 'M')
nb_put8(b, 1, 'L')
@ -77,201 +75,201 @@ program NetTest {
p.port = port
p.len = o + 6
p.data = b
push(nettest_st.inbound, p)
push(inbound, p)
}
function frame(base_st: mut BaseState, net_st: mut NetState, dt: float) -> void {
net_begin(base_st, net_st, dt)
net_update(base_st, net_st, dt)
net_end(base_st, net_st)
function frame(dt: float) -> void {
net_begin(dt)
net_update(dt)
net_end()
}
# the host with a guest let in at 10.0.0.9:5000 as pid 2
function host_with_guest(base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) -> int {
fresh(base_st, net_st, nettest_st)
expect(net_host(net_st, 27415, 77))
arrive(nettest_st, Udp.ip("10.0.0.9"), 5000, 0, 0, 0, NET_HELLO, 1, 42)
net_begin(base_st, net_st, 0.01)
let got = q_drain(base_st, net_inbox(base_st, net_st))
function host_with_guest() -> int {
fresh()
expect(net_host(27415, 77))
arrive(Udp.ip("10.0.0.9"), 5000, 0, 0, 0, NET_HELLO, 1, 42)
net_begin(0.01)
let got = q_drain(net_inbox())
expect_eq(len(got), 1)
return net_accept(net_st, got[0].slot)
return net_accept(got[0].slot)
}
function facts_of(base_st: mut BaseState, net_st: mut NetState, what: int) -> []NetFact {
let all = q_drain(base_st, net_facts(base_st, net_st))
function facts_of(what: int) -> []NetFact {
let all = q_drain(net_facts())
let out = new []NetFact
for i in 0 .. len(all) { if all[i].what == what { push(out, all[i]) } }
return out
}
test "the codec carries ints, bools, floats and text, and a short message reads bad" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
fresh(base_st, net_st, nettest_st)
nw_begin(net_st)
nw_i(net_st, -7)
nw_b(net_st, true)
nw_f(net_st, 2.5)
nw_s(net_st, "maroon")
net_read(net_st, net_written(net_st))
expect_eq(nr_i(net_st), -7)
expect(nr_b(net_st))
expect_near(nr_f(net_st), 2.5, 0.0001)
expect(nr_s(net_st) == "maroon")
expect(not nr_bad(net_st))
expect_eq(nr_i(net_st), 0)
expect(nr_bad(net_st))
test "the codec carries ints, bools, floats and text, and a short message reads bad" {
fresh()
nw_begin()
nw_i(-7)
nw_b(true)
nw_f(2.5)
nw_s("maroon")
net_read(net_written())
expect_eq(nr_i(), -7)
expect(nr_b())
expect_near(nr_f(), 2.5, 0.0001)
expect(nr_s() == "maroon")
expect(not nr_bad())
expect_eq(nr_i(), 0)
expect(nr_bad())
}
test "a join code decodes to its address and port; an address and a room code are taken too" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
fresh(base_st, net_st, nettest_st)
test "a join code decodes to its address and port; an address and a room code are taken too" {
fresh()
let ip = Udp.ip("192.168.1.20")
let code = net_code_of(ip, 27415)
expect_eq(len(code), 11)
expect(net_parse_code(net_st, code))
expect_eq(net_code_ip(net_st), ip)
expect_eq(net_code_port(net_st), 27415)
expect(net_parse_code(net_st, Text.lower(code)))
expect(net_parse_code(net_st, "10.1.2.3:4000"))
expect_eq(net_code_port(net_st), 4000)
expect(net_parse_code(net_st, "10.1.2.3"))
expect_eq(net_code_port(net_st), 27415)
expect(not net_parse_code(net_st, "HELLO"))
expect(net_parse_code(code))
expect_eq(net_code_ip(), ip)
expect_eq(net_code_port(), 27415)
expect(net_parse_code(Text.lower(code)))
expect(net_parse_code("10.1.2.3:4000"))
expect_eq(net_code_port(), 4000)
expect(net_parse_code("10.1.2.3"))
expect_eq(net_code_port(), 27415)
expect(not net_parse_code("HELLO"))
expect(net_is_room("abcdef"))
expect(not net_is_room("ABCDE1"))
}
test "a host takes a stranger only with a hello, and hands out pids from 2" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
let pid = host_with_guest(base_st, net_st, nettest_st)
test "a host takes a stranger only with a hello, and hands out pids from 2" {
let pid = host_with_guest()
expect_eq(pid, 2)
expect(net_hosting(net_st))
expect_eq(net_my_pid(net_st), 1)
arrive(nettest_st, Udp.ip("10.0.0.10"), 6000, 0, 0, 0, 9, 1, 1)
net_begin(base_st, net_st, 0.01)
expect_eq(q_len(net_inbox(base_st, net_st)), 0)
arrive(nettest_st, Udp.ip("10.0.0.11"), 6000, 0, 0, 0, NET_HELLO, 1, 1)
net_begin(base_st, net_st, 0.01)
let got = q_drain(base_st, net_inbox(base_st, net_st))
expect_eq(net_accept(net_st, got[0].slot), 3)
expect(net_hosting())
expect_eq(net_my_pid(), 1)
arrive(Udp.ip("10.0.0.10"), 6000, 0, 0, 0, 9, 1, 1)
net_begin(0.01)
expect_eq(q_len(net_inbox()), 0)
arrive(Udp.ip("10.0.0.11"), 6000, 0, 0, 0, NET_HELLO, 1, 1)
net_begin(0.01)
let got = q_drain(net_inbox())
expect_eq(net_accept(got[0].slot), 3)
}
test "a message's payload reads back as it was written" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
host_with_guest(base_st, net_st, nettest_st)
arrive(nettest_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 2, 1234)
net_begin(base_st, net_st, 0.01)
let got = q_drain(base_st, net_inbox(base_st, net_st))
test "a message's payload reads back as it was written" {
host_with_guest()
arrive(Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 2, 1234)
net_begin(0.01)
let got = q_drain(net_inbox())
expect_eq(len(got), 1)
expect_eq(got[0].kind, 9)
expect_eq(got[0].from, 2)
net_read(net_st, got[0])
expect_eq(nr_i(net_st), 1234)
expect(not nr_bad(net_st))
nr_i(net_st)
expect(nr_bad(net_st))
net_read(got[0])
expect_eq(nr_i(), 1234)
expect(not nr_bad())
nr_i()
expect(nr_bad())
}
test "a reliable message that arrives early is dropped and taken in order" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
let slot = np_of_pid(net_st, host_with_guest(base_st, net_st, nettest_st))
arrive(nettest_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 3, 30)
net_begin(base_st, net_st, 0.01)
expect_eq(q_len(net_inbox(base_st, net_st)), 0)
arrive(nettest_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 2, 20)
arrive(nettest_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 3, 30)
net_begin(base_st, net_st, 0.01)
let got = q_drain(base_st, net_inbox(base_st, net_st))
test "a reliable message that arrives early is dropped and taken in order" {
let slot = np_of_pid(host_with_guest())
arrive(Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 3, 30)
net_begin(0.01)
expect_eq(q_len(net_inbox()), 0)
arrive(Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 2, 20)
arrive(Udp.ip("10.0.0.9"), 5000, 77, 2, 0, 9, 3, 30)
net_begin(0.01)
let got = q_drain(net_inbox())
expect_eq(len(got), 2)
net_read(net_st, got[1])
expect_eq(nr_i(net_st), 30)
net_read(got[1])
expect_eq(nr_i(), 30)
expect_eq(got[1].slot, slot)
}
test "a reliable message goes again until it is taken" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
let slot = np_of_pid(net_st, host_with_guest(base_st, net_st, nettest_st))
net_end(base_st, net_st)
nettest_st.sent = new []Pkt
nw_begin(net_st)
nw_i(net_st, 5)
np_queue(net_st, slot, 9, true)
frame(base_st, net_st, 0.01)
expect_eq(len(nettest_st.sent), 1)
frame(base_st, net_st, 0.1)
expect_eq(len(nettest_st.sent), 1)
frame(base_st, net_st, 0.2)
expect_eq(len(nettest_st.sent), 2)
arrive(nettest_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 1, 9, 0, 0)
frame(base_st, net_st, 0.3)
let n = len(nettest_st.sent)
frame(base_st, net_st, 0.3)
expect_eq(len(nettest_st.sent), n)
test "a reliable message goes again until it is taken" {
let slot = np_of_pid(host_with_guest())
net_end()
sent = new []Pkt
nw_begin()
nw_i(5)
np_queue(slot, 9, true)
frame(0.01)
expect_eq(len(sent), 1)
frame(0.1)
expect_eq(len(sent), 1)
frame(0.2)
expect_eq(len(sent), 2)
arrive(Udp.ip("10.0.0.9"), 5000, 77, 2, 1, 9, 0, 0)
frame(0.3)
let n = len(sent)
frame(0.3)
expect_eq(len(sent), n)
}
test "an unreliable message half a second old is dropped" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
let slot = np_of_pid(net_st, host_with_guest(base_st, net_st, nettest_st))
net_end(base_st, net_st)
nettest_st.sent = new []Pkt
nw_begin(net_st)
np_queue(net_st, slot, 9, false)
net_begin(base_st, net_st, 0.6)
net_end(base_st, net_st)
expect_eq(len(nettest_st.sent), 0)
test "an unreliable message half a second old is dropped" {
let slot = np_of_pid(host_with_guest())
net_end()
sent = new []Pkt
nw_begin()
np_queue(slot, 9, false)
net_begin(0.6)
net_end()
expect_eq(len(sent), 0)
}
test "a guest sends its hello until nobody has answered for too long" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
fresh(base_st, net_st, nettest_st)
nw_begin(net_st)
nw_s(net_st, "v1")
net_hello_set(net_st)
expect(net_join(net_st, Udp.ip("10.0.0.1"), 27415))
expect(net_joining(net_st))
frame(base_st, net_st, 0.1)
expect_eq(len(nettest_st.sent), 1)
expect_eq(nb_get8(nettest_st.sent[0].data, 20), NET_HELLO)
for i in 0 .. 110 { frame(base_st, net_st, 0.1) }
expect_eq(net_state(net_st), NET_LOST)
let down = facts_of(base_st, net_st, NET_DOWN)
test "a guest sends its hello until nobody has answered for too long" {
fresh()
nw_begin()
nw_s("v1")
net_hello_set()
expect(net_join(Udp.ip("10.0.0.1"), 27415))
expect(net_joining())
frame(0.1)
expect_eq(len(sent), 1)
expect_eq(nb_get8(sent[0].data, 20), NET_HELLO)
for i in 0 .. 110 { frame(0.1) }
expect_eq(net_state(), NET_LOST)
let down = facts_of(NET_DOWN)
expect_eq(len(down), 1)
expect_eq(down[0].why, NET_WHY_NO_ANSWER)
net_fresh(net_st)
expect_eq(net_state(net_st), NET_IDLE)
net_fresh()
expect_eq(net_state(), NET_IDLE)
}
test "a welcome makes the guest that pid in that session" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
fresh(base_st, net_st, nettest_st)
nw_begin(net_st)
net_hello_set(net_st)
net_join(net_st, Udp.ip("10.0.0.1"), 27415)
net_joined_as(net_st, 99, 4)
expect(net_live(net_st))
expect_eq(net_my_pid(net_st), 4)
expect_eq(net_token(net_st), 99)
test "a welcome makes the guest that pid in that session" {
fresh()
nw_begin()
net_hello_set()
net_join(Udp.ip("10.0.0.1"), 27415)
net_joined_as(99, 4)
expect(net_live())
expect_eq(net_my_pid(), 4)
expect_eq(net_token(), 99)
}
test "a guest silent for the timeout is gone" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
let pid = host_with_guest(base_st, net_st, nettest_st)
let slot = np_of_pid(net_st, pid)
for i in 0 .. 105 { frame(base_st, net_st, 0.1) }
let gone = facts_of(base_st, net_st, NET_GONE)
test "a guest silent for the timeout is gone" {
let pid = host_with_guest()
let slot = np_of_pid(pid)
for i in 0 .. 105 { frame(0.1) }
let gone = facts_of(NET_GONE)
expect_eq(len(gone), 1)
expect_eq(gone[0].pid, pid)
expect_eq(gone[0].slot, slot)
expect(not net_peer_on(net_st, slot))
expect(not net_peer_on(slot))
}
test "a slot is let go once it has taken its last message" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
let slot = np_of_pid(net_st, host_with_guest(base_st, net_st, nettest_st))
nw_begin(net_st)
np_queue(net_st, slot, 9, true)
net_close_when_sent(net_st, slot, 20.0)
frame(base_st, net_st, 0.01)
expect(net_peer_on(net_st, slot))
arrive(nettest_st, Udp.ip("10.0.0.9"), 5000, 77, 2, 1, 9, 0, 0)
frame(base_st, net_st, 0.01)
expect(not net_peer_on(net_st, slot))
expect_eq(len(facts_of(base_st, net_st, NET_CLOSED)), 1)
test "a slot is let go once it has taken its last message" {
let slot = np_of_pid(host_with_guest())
nw_begin()
np_queue(slot, 9, true)
net_close_when_sent(slot, 20.0)
frame(0.01)
expect(net_peer_on(slot))
arrive(Udp.ip("10.0.0.9"), 5000, 77, 2, 1, 9, 0, 0)
frame(0.01)
expect(not net_peer_on(slot))
expect_eq(len(facts_of(NET_CLOSED)), 1)
}
test "with no matchmaker, a room code is refused" (base_st: mut BaseState, net_st: mut NetState, nettest_st: mut NettestState) {
fresh(base_st, net_st, nettest_st)
expect(not net_room_on(net_st))
expect(not net_room_join(net_st, "ABCDEF"))
expect_eq(net_why(net_st), NET_WHY_NO_MATCHMAKER)
test "with no matchmaker, a room code is refused" {
fresh()
expect(not net_room_on())
expect(not net_room_join("ABCDEF"))
expect_eq(net_why(), NET_WHY_NO_MATCHMAKER)
}
}