ludic/packages/ludic.session
Orkuncakilkaya 71735b10a2 feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune
ludic.base's Queue<T> carries a QueueTag (its name and pending count): queue_new(name), q_push(q, v),
q_drain(q), q_clear(q) take no BaseState, and core_undrained(tags) names the given queues still holding
facts. A reducer on a package's state can now call that package's verbs (wallet_earn(wallet_st, n)).

ludic migrate state --prune (ludicc --migrate-prune) takes out each state parameter a function no
longer uses, nor anything it calls, and the argument that fills it - including an argument for a
parameter the callee has dropped, which is taken out before the call is checked, so a generic's T is
told by the argument that says it. A reducer keeps its state. --dry-run now counts the edits it would
make. Every package was moved with it: 608 base_st parameters and their arguments, 1889 edits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 04:19:56 +03:00
..
tests feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
index.ludic feat(packages): ludic.session - a party's roster (slot 0 a port, the others as reported), the roles and who owns the world, the shared night, votes and a world-changing act asking the party; joins, leaves, roles, votes and the night as facts 2026-09-25 07:45:26 +03:00
near.ludic wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
night.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
package.ludic feat(packages): ludic.session - a party's roster (slot 0 a port, the others as reported), the roles and who owns the world, the shared night, votes and a world-changing act asking the party; joins, leaves, roles, votes and the night as facts 2026-09-25 07:45:26 +03:00
README.md feat(packages): ludic.session - a party's roster (slot 0 a port, the others as reported), the roles and who owns the world, the shared night, votes and a world-changing act asking the party; joins, leaves, roles, votes and the night as facts 2026-09-25 07:45:26 +03:00
roles.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
roster.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
state.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
vote_state.ludic wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00
votes.ludic feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune 2026-09-26 04:19:56 +03:00
where.ludic wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run 2026-09-25 15:31:34 +03:00

ludic.session

Who is in a party and who runs the world, whatever carries its messages: the roster, the roles, the shared night, votes, and "a world-changing act asks the party". It sends nothing - what the other machines have to hear comes out as facts, and the game puts them on its wire. Uses ludic.base and nothing else.

import "ludic.session"

The roster

Slot 0 is this machine's player, read live through the SessionLocal port; slots 1 to PLY_MAX - 1 are remote players, holding what their own machines reported (where they stand, their heading and speed, how much their rest speeds the clock, whether they have turned in, their name). World code asks the roster, never "the player": for p in 0 .. PLY_MAX { if ply_on(p) ... ply_x(p) ... }, or ply_near(x, z) and ply_near_i().

port SessionLocal {
  x, y, z: fn() -> float                   # required
  yaw, speed: fn() -> float (0), rest: fn() -> float (1), name: fn() -> string ("")
}

Roles

SES_SOLO owns the world and has no company; SES_HOST owns it and has company; SES_GUEST owns only its own player. ses_owns_world(), ses_live(), ses_night_passes() (alone, lying down ends the night). The transport says what this machine became (ses_set_role).

API

ply_init(), ply_join(pid) -> slot, ply_leave(p), ply_leave_all() the roster (-1: full, or pid 0)
ply_report(p, x, y, z, yaw, speed), ply_report_rest(p, rest, sleeping), ply_report_name(p, name) what a remote machine said
ply_on, ply_is_local, ply_slot_of(pid), ply_count, ply_pid, ply_name, ply_x/y/z/yaw/speed/rest(p), ply_sleeping(p) who and where
ply_near(x, z), ply_near_d2, ply_near_i(), ply_dist(p, x, z) the nearest player
ses_role(), ses_set_role(r), ses_owns_world(), ses_live(), ses_hosting(), ses_guest(), ses_night_passes() roles
ses_set_party(max, open), ses_set_open(b), ses_max(), ses_open(), ses_room_for_one() the party a host allows
ses_sleep(on) -> bool, ses_sleeping(), ses_sleep_ready(p, on), ses_night_check(), ses_night_passed(), ses_sleep_waiting() the shared night: false alone; the host counts
ses_may(kind, param, text) -> SES_MAY_GO / _ASKED / _BUSY / _HOSTS a world-changing act
ses_vote_open, ses_vote_cast, ses_vote_heard(id, yes), ses_vote_told(id, kind, text), ses_vote_ended(id, passed), ses_vote_tick(dt) a majority of the players, the host included, within thirty seconds
ses_vote_live/id/kind/text/left/yes/no/mine/passed() the vote as it stands
ses_reset() everyone else gone, alone, nothing asked, nobody asleep
ses_facts() -> Queue<SessionFact> SES_JOINED / SES_LEFT (slot, pid), SES_ROLE (role), SES_VOTE_OPENED, SES_VOTE_CAST (a guest's answer to send), SES_VOTE_RESOLVED (yes: passed), SES_NIGHT (the host: everyone is in), SES_SLEEP (this player turned in or got up)

It is not a System: a party exists before any world does (a join from the title), so the game calls ses_vote_tick and drains ses_facts() in its own frame.

Tests

ludic test packages/ludic.session

A fake local player: the roster filling and emptying, the nearest player, roles, the party's size, the night, votes passing, failing and timing out, and a guest's side of each.