diff --git a/packages/ludic.net/codes.ludic b/packages/ludic.net/codes.ludic index 2f9e2813..250ae4ed 100644 --- a/packages/ludic.net/codes.ludic +++ b/packages/ludic.net/codes.ludic @@ -3,6 +3,7 @@ # six of the same letters. const NET_ALPHA: string = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" +@alloc_ok("once per join code shown") export function net_code_of(ip: int, port: int) -> string { let a = NET_ALPHA let buf = buffer(11) diff --git a/packages/ludic.net/peers.ludic b/packages/ludic.net/peers.ludic index 440304d7..6f5fb982 100644 --- a/packages/ludic.net/peers.ludic +++ b/packages/ludic.net/peers.ludic @@ -59,7 +59,7 @@ export function np_queue(net_st: mut NetState, slot: int, kind: int, rel: bool) m.id = p.next_id p.next_id += 1 } - push(p.queue, m) + kept_push(p.queue, m) } # to everyone this machine talks to (a host: every guest but `except`; a guest: the host) diff --git a/packages/ludic.net/pool.ludic b/packages/ludic.net/pool.ludic index 3b30b7cf..53a7b7df 100644 --- a/packages/ludic.net/pool.ludic +++ b/packages/ludic.net/pool.ludic @@ -3,14 +3,12 @@ # taken or goes stale; an inbox one comes back two drains after it was handed out function net__msg_take(net_st: mut NetState) -> NetMsg { if len(net_st.net__free) > 0 { return List.pop(net_st.net__free) } - let m = new NetMsg - m.data = buffer(NET_MTU) - return m + return net__msg_new() } # everything a peer had queued, back to the pool (its slot opened again, or let go) function net__queue_free(net_st: mut NetState, p: NetPeer) -> void { - for i in 0 .. len(p.queue) { push(net_st.net__free, p.queue[i]) } + for i in 0 .. len(p.queue) { kept_push(net_st.net__free, p.queue[i]) } List.clear(p.queue) } @@ -24,9 +22,7 @@ function net__in_take(net_st: mut NetState) -> NetMessage { var pool = net_st.net__ina if net_st.net__in_on_b { pool = net_st.net__inb } if net_st.net__inn >= len(pool) { - let m = new NetMessage - m.data = buffer(NET_MTU) - push(pool, m) + net__in_grow(pool) } let m = pool[net_st.net__inn] net_st.net__inn += 1 @@ -34,3 +30,31 @@ function net__in_take(net_st: mut NetState) -> NetMessage { } export function net_spare_count(net_st: NetState) -> int { return len(net_st.net__free) } + +@alloc_ok("a pool miss: past the most messages in flight at once only, then kept") +function net__msg_new() -> NetMsg { + let m = new NetMsg + m.data = buffer(NET_MTU) + return m +} + +@alloc_ok("a pool miss: past the most messages in one drain only, then kept") +function net__in_grow(pool: []NetMessage) -> void { + let m = new NetMessage + m.data = buffer(NET_MTU) + push(pool, m) +} + +# a fact's record, from a ring made with the state: far more than a frame reports between drains +const NET_FACTS_MOST: int = 64 +function net__fpool__new() -> []NetFact { + let xs = new []NetFact + for i in 0 .. NET_FACTS_MOST { push(xs, new NetFact) } + return xs +} + +function net__fact_rec(net_st: mut NetState) -> NetFact { + let f = net_st.net__fpool[net_st.net__fat] + net_st.net__fat = (net_st.net__fat + 1) % len(net_st.net__fpool) + return f +} diff --git a/packages/ludic.net/ports.ludic b/packages/ludic.net/ports.ludic index d0b316ae..c11b887f 100644 --- a/packages/ludic.net/ports.ludic +++ b/packages/ludic.net/ports.ludic @@ -69,8 +69,8 @@ export function net_facts(net_st: NetState) -> Queue { return net_st.net__facts } -function net__fact(net_st: NetState, what: int, slot: int, pid: int, why: int, text: string) -> void { - let f = new NetFact +function net__fact(net_st: mut NetState, what: int, slot: int, pid: int, why: int, text: string) -> void { + let f = net__fact_rec(net_st) f.what = what f.slot = slot f.pid = pid diff --git a/packages/ludic.net/punch.ludic b/packages/ludic.net/punch.ludic index c0c71e7c..a75d89bf 100644 --- a/packages/ludic.net/punch.ludic +++ b/packages/ludic.net/punch.ludic @@ -2,6 +2,7 @@ # expects the guest, while the guest's hello goes the other way: both routers have seen traffic # go out, so the replies come in +@alloc_ok("once per guest punched for") function net__new_guest(net_st: mut NetState, pub: string, local: string) -> void { let key = `{pub}|{local}` for i in 0 .. len(net_st.net__seen) { if net_st.net__seen[i] == key { return } } @@ -11,6 +12,7 @@ function net__new_guest(net_st: mut NetState, pub: string, local: string) -> voi if net_parse_code(net_st, local) { net__punch_add(net_st, net_st.net__code_ip, net_st.net__code_port) } } +@alloc_ok("once per guest punched for") function net__punch_add(net_st: mut NetState, ip: int, port: int) -> void { if net_st.net__punch_ip == null { net_st.net__punch_ip = words(16) @@ -34,6 +36,7 @@ function net__punch_tick(net_st: mut NetState, dt: float) -> void { if net_st.net__punch_t > 0.0 { return } net_st.net__punch_t = 0.5 if net_st.net__punch_b == null { + @alloc_ok("once: the punch's four bytes") net_st.net__punch_b = buffer(4) nb_put8(net_st.net__punch_b, 0, 'M') nb_put8(net_st.net__punch_b, 1, 'P') diff --git a/packages/ludic.net/receive.ludic b/packages/ludic.net/receive.ludic index f3d091cc..e2459d9f 100644 --- a/packages/ludic.net/receive.ludic +++ b/packages/ludic.net/receive.ludic @@ -12,6 +12,7 @@ function net__pump(net_st: mut NetState) -> void { } function net__packet(net_st: mut NetState, n: int, ip: int, port: int) -> void { + @alloc_ok("only while tracing every packet") 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 diff --git a/packages/ludic.net/relay.ludic b/packages/ludic.net/relay.ludic index 6fdb4a78..d505ac55 100644 --- a/packages/ludic.net/relay.ludic +++ b/packages/ludic.net/relay.ludic @@ -30,6 +30,7 @@ function net__relay_hello(net_st: NetState) -> void { NetSocket.send(net_st.net__sock, net_st.net__relay_ip, net_st.net__relay_port, b, 10) } +@alloc_ok("once, when the room falls back to the relay") 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`) diff --git a/packages/ludic.net/room.ludic b/packages/ludic.net/room.ludic index fb8e9e3e..29ac7905 100644 --- a/packages/ludic.net/room.ludic +++ b/packages/ludic.net/room.ludic @@ -15,6 +15,7 @@ export function net_room(net_st: NetState) -> string { return net_st.net__room } export function net_room_asking(net_st: NetState) -> bool { return net_st.net__r_state == NET_R_STUN or net_st.net__r_state == NET_R_ASK } # the host (after net_host or net_become_host): a room at the matchmaker, if there is one +@alloc_ok("once per room opened") export function net_room_host(net_st: mut NetState) -> void { if not net_room_on(net_st) { return } net_st.net__r_role = 1 @@ -51,6 +52,7 @@ function net__stun_begin(net_st: mut NetState) -> void { net__stun_send(net_st) } +@alloc_ok("once per request to the party server") function net__room_body(net_st: NetState, role_key: string) -> string { var pub = "" if net_st.net__pub_ip != 0 { pub = net__addr_text(net_st.net__pub_ip, net_st.net__pub_port) } @@ -62,6 +64,7 @@ function net__json_str(v: Val, key: string) -> string { return value_as_str(value_get(v, key)) } +@alloc_ok("a STUN answer or a log line: a few per session") function net__addr_text(ip: int, port: int) -> string { return `{Udp.ip_text(ip)}:{port}` } function net__local_text(net_st: NetState) -> string { diff --git a/packages/ludic.net/room_connect.ludic b/packages/ludic.net/room_connect.ludic index 34eca1e7..cb454116 100644 --- a/packages/ludic.net/room_connect.ludic +++ b/packages/ludic.net/room_connect.ludic @@ -1,6 +1,7 @@ # room_connect.ludic - the matchmaker's answer: a guest dials the host it named (or is out), # and leaving lets the room go # the matchmaker let this machine down: a guest is out, a host keeps its code-less party +@alloc_ok("once, when a room cannot be had") function net__room_fail(net_st: mut NetState, why: int) -> void { net__log(`matchmaker: {why}`) let was_guest = net_st.net__r_role == 2 @@ -11,6 +12,7 @@ function net__room_fail(net_st: mut NetState, why: int) -> void { } # the guest: to the host's outside address, or its inside one behind the same router +@alloc_ok("once per room answer") function net__room_connect(net_st: mut NetState, host_pub: string, host_local: string) -> void { var target = host_pub var same_router = false diff --git a/packages/ludic.net/room_tick.ludic b/packages/ludic.net/room_tick.ludic index 19b6a1e2..9e0a7093 100644 --- a/packages/ludic.net/room_tick.ludic +++ b/packages/ludic.net/room_tick.ludic @@ -23,6 +23,7 @@ function net__room_tick(net_st: mut NetState, dt: float) -> void { } # a second and a half for an answer, asked three times; without one, inside addresses only +@alloc_ok("a request to the party server: once per room stage") function net__room_stun_tick(net_st: mut NetState) -> void { if net_st.net__pub_ip == 0 and net_st.net__r_t > 0.5 and net_st.net__r_tries < 3 { net_st.net__r_tries += 1 @@ -63,6 +64,8 @@ function net__room_answer(net_st: mut NetState) -> void { return } net_st.net__room = code + @alloc_ok("once per room") + net_st.net__watch_url = `{net__conf(net_st).signal_url}/rooms/{code}` net__take_relay(net_st, v) net_st.net__r_state = NET_R_ROOM net_st.net__poll_t = 0.0 @@ -100,7 +103,7 @@ function net__room_watch(net_st: mut NetState, dt: float) -> void { net_st.net__poll_t = net_st.net__poll_t - dt if net_st.net__poll_t < 0.0 { net_st.net__poll_t = 1.0 - net_st.net__http = Http.get(`{net__conf(net_st).signal_url}/rooms/{net_st.net__room}`) + net_st.net__http = Http.get(net_st.net__watch_url) net_st.net__r_t = 0.0 } } diff --git a/packages/ludic.net/send.ludic b/packages/ludic.net/send.ludic index 4a414d8c..3eafbbf9 100644 --- a/packages/ludic.net/send.ludic +++ b/packages/ludic.net/send.ludic @@ -21,7 +21,7 @@ function net__flush_one(net_st: mut NetState, p: NetPeer) -> bool { let taken = m.rel and m.id <= p.acked let stale = not m.rel and net_st.net__time - m.made > 0.5 if taken or stale { - push(net_st.net__free, m) # back to the pool + kept_push(net_st.net__free, m) # back to the pool continue } var due = true @@ -33,9 +33,9 @@ function net__flush_one(net_st: mut NetState, p: NetPeer) -> bool { count += 1 m.sent = net_st.net__time if net_st.net__kind_out != null and m.kind < 64 { net_st.net__kind_out[m.kind] += size } - if m.rel { push(keep, m) } else { push(net_st.net__free, m) } # a reliable one kept until acknowledged + if m.rel { kept_push(keep, m) } else { kept_push(net_st.net__free, m) } # a reliable one kept until acknowledged } else { - push(keep, m) # not due, or waits for the next packet + kept_push(keep, m) # not due, or waits for the next packet } } p.back = p.queue @@ -51,6 +51,7 @@ function net__flush_one(net_st: mut NetState, p: NetPeer) -> bool { nb_put32(net_st.net__out, 16, count) let sent = net__relay_send(net_st, p, o) if sent > 0 { net_st.net__bytes_out += sent + 28 } # with the IP and UDP headers: what the line carries + @alloc_ok("only while tracing every packet") if NetWorld.trace() { net__log(`out {o} bytes to {Udp.ip_text(p.ip)}:{p.port}: {sent}`) } p.ack_due = false return count > 0 @@ -75,7 +76,10 @@ function net__put_msg(net_st: NetState, at: int, m: NetMsg) -> int { # the per-kind byte count starts again (a game measuring what a party costs its upload) export function net_kind_out_clear(net_st: mut NetState) -> void { - net_st.net__kind_out = words(64) + if net_st.net__kind_out == null { + @alloc_ok("once: the counts are made the first time they are cleared") + net_st.net__kind_out = words(64) + } for k in 0 .. 64 { net_st.net__kind_out[k] = 0 } } diff --git a/packages/ludic.net/session.ludic b/packages/ludic.net/session.ludic index 22896df5..f9075bd9 100644 --- a/packages/ludic.net/session.ludic +++ b/packages/ludic.net/session.ludic @@ -2,6 +2,7 @@ # machine's pid is what its packets say. A token picked by the host names the session. # open a socket on `port` and take guests; false (net_why) when the port is taken +@alloc_ok("once per party hosted") export function net_host(net_st: mut NetState, port: int, token: int) -> bool { if net_live(net_st) { return true } net_st.net__sock = NetSocket.open(port) @@ -18,6 +19,7 @@ export function net_host(net_st: mut NetState, port: int, token: int) -> bool { } # dial a host at an address; the hello (net_hello_set) goes until a welcome or a refusal +@alloc_ok("once per join") export function net_join(net_st: mut NetState, ip: int, port: int) -> bool { if net_live(net_st) { return false } if not net__socket(net_st) { return false } diff --git a/packages/ludic.net/state.ludic b/packages/ludic.net/state.ludic index 12fd4195..909d0c47 100644 --- a/packages/ludic.net/state.ludic +++ b/packages/ludic.net/state.ludic @@ -51,6 +51,9 @@ export property NetMessage { export state NetState { net__cfg: NetConfig = new NetConfig net__facts: Queue = net__facts__new() + net__fpool: []NetFact = net__fpool__new() # the facts' records, taken in turn (pool.ludic) + net__fat: int = 0 + net__watch_url: string = "" # the room's address, made once per room net__seen: []string = new []string # guests already punched for net__free: []NetMsg = new []NetMsg # sent and taken (or stale): made again, never new (pool.ludic) net__ina: []NetMessage = new []NetMessage # the inbox's records, two halves diff --git a/packages/ludic.net/tick.ludic b/packages/ludic.net/tick.ludic index 1b731856..dc15119a 100644 --- a/packages/ludic.net/tick.ludic +++ b/packages/ludic.net/tick.ludic @@ -99,6 +99,7 @@ export function net_reset(net_st: mut NetState) -> void { 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 diff --git a/packages/ludic.shop/demand.ludic b/packages/ludic.shop/demand.ludic index 65773f59..6386e762 100644 --- a/packages/ludic.shop/demand.ludic +++ b/packages/ludic.shop/demand.ludic @@ -25,11 +25,12 @@ function shop_pick_one(shop_st: ShopState, v: int, taken: []int) -> int { } function shop_roll_vendor(shop_st: mut ShopState, v: int) -> void { - let taken = new []int + let taken = shop_st.shop_taken + List.clear(taken) for k in 0 .. shop_st.shop_wants_n { let it = shop_pick_one(shop_st, v, taken) shop_st.shop_want_list[v * shop_st.shop_wants_n + k] = it - if it >= 0 { push(taken, it) } + if it >= 0 { kept_push(taken, it) } } shop_st.shop_full_list[v] = shop_pick_one(shop_st, v, taken) } @@ -39,11 +40,8 @@ export function shop_roll(shop_st: mut ShopState) -> void { let week = shop_week(shop_st) if week == shop_st.shop_week_rolled and shop_st.shop_want_list != null { return } shop_st.shop_week_rolled = week - shop_st.shop_dice = rng_new(shop_st.shop_seed + week * 37) - shop_st.shop_want_list = new []int - shop_st.shop_full_list = new []int - for i in 0 .. shop_vendors(shop_st) * shop_st.shop_wants_n { push(shop_st.shop_want_list, -1) } - for v in 0 .. shop_vendors(shop_st) { push(shop_st.shop_full_list, -1) } + rng_seed(shop_st.shop_dice, shop_st.shop_seed + week * 37) + shop_lists_empty(shop_st) if shop_items(shop_st) == 0 { return } for v in 0 .. shop_vendors(shop_st) { shop_roll_vendor(shop_st, v) } } @@ -90,3 +88,4 @@ function shop_demand_reset(shop_st: mut ShopState) -> void { shop_st.shop_want_list = null shop_st.shop_full_list = null } + diff --git a/packages/ludic.shop/index.ludic b/packages/ludic.shop/index.ludic index 9464999a..c4b9186a 100644 --- a/packages/ludic.shop/index.ludic +++ b/packages/ludic.shop/index.ludic @@ -5,6 +5,7 @@ numbers float import "ludic.base" import "state.ludic" import "demand.ludic" +import "week_lists.ludic" import "prices.ludic" import "trade.ludic" import "system.ludic" diff --git a/packages/ludic.shop/state.ludic b/packages/ludic.shop/state.ludic index 01f423d7..2a3aab4d 100644 --- a/packages/ludic.shop/state.ludic +++ b/packages/ludic.shop/state.ludic @@ -40,7 +40,8 @@ export state ShopState { shop_week_rolled: int = -1 shop_want_list: []int = null # shop_wants_n per vendor, -1 empty shop_full_list: []int = null # one per vendor, -1 none - shop_dice: Rng = null + shop_dice: Rng = rng_new(0) + shop_taken: []int = new []int # a vendor's picks while its week is rolled, kept shop_base: []int = null # an item's base price shop_sell_frac: []float = null # what fraction of the curve a vendor pays for it shop_vendor_list: []ShopVendor = null diff --git a/packages/ludic.shop/week_lists.ludic b/packages/ludic.shop/week_lists.ludic new file mode 100644 index 00000000..784c957c --- /dev/null +++ b/packages/ludic.shop/week_lists.ludic @@ -0,0 +1,14 @@ +# ludic.shop/week_lists.ludic - the lists a week is rolled into, made once and cleared after +# the week's lists, every entry -1: made the first week and cleared after, the vendors being fixed +function shop_lists_empty(shop_st: mut ShopState) -> void { + if shop_st.shop_want_list == null { + @alloc_ok("once: the lists are made the first week they are rolled") + shop_st.shop_want_list = new []int + @alloc_ok("once: as above") + shop_st.shop_full_list = new []int + } + List.clear(shop_st.shop_want_list) + List.clear(shop_st.shop_full_list) + for i in 0 .. shop_vendors(shop_st) * shop_st.shop_wants_n { kept_push(shop_st.shop_want_list, -1) } + for v in 0 .. shop_vendors(shop_st) { kept_push(shop_st.shop_full_list, -1) } +}