feat(ludic.base): Systems is an open registry - def a system from any module
The runner's list starts from the declared systems, in the order the compiler gives an open registry, and core_add appends after them. registry_test covers it; the README says ludic test runs the package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
73129b59d5
commit
cac8c740fa
3 changed files with 43 additions and 12 deletions
|
|
@ -43,6 +43,7 @@ import "ludic.base"
|
|||
| `sv_put_int`, `sv_put_float`, `sv_put_bool`, `sv_put_str`, `sv_put_ints` | the matching writes |
|
||||
| `System { key, phase, version, init, reset, tick, save, load }`, `system_new(key, phase)` | a system; a null function is a verb it does not have |
|
||||
| `core_add(s)`, `core_clear()`, `core_count()` | the game's system list (a key may appear once) |
|
||||
| `def Systems key { ... }`, `SYS_<KEY>`, `SYS_COUNT` | a system declared from any module; the list starts from these |
|
||||
| `core_init_all()`, `core_reset_all()`, `core_tick_all(t)`, `core_save_all() -> Val`, `core_load_all(v)` | the runner: in the order added, tick phase by phase |
|
||||
|
||||
## A toy mechanic
|
||||
|
|
@ -123,24 +124,26 @@ Both are compiled and run by `tests/route_test.ludic` (the mechanics are `tests/
|
|||
|
||||
## Tests
|
||||
|
||||
Each piece has a program under `tests/`, built and run directly:
|
||||
Each piece has a program under `tests/`, and `ludic test` runs them all, every test block in a
|
||||
process of its own:
|
||||
|
||||
```bash
|
||||
ludic build packages/ludic.base/tests/queue_test.ludic --headless -o /tmp/queue_test && /tmp/queue_test
|
||||
ludic test packages/ludic.base
|
||||
```
|
||||
|
||||
`queue_test`, `rng_test`, `save_test`, `system_test` and `route_test`. Two things the language
|
||||
does not do yet shape them: a generic call inside a `test` body is not resolved (so the queue
|
||||
cases are functions a test calls), and test blocks share one program's globals (so each case
|
||||
that uses the runner starts with `core_clear()`); `ludic test <dir>` with a fresh state per
|
||||
test will lift the second.
|
||||
`queue_test`, `rng_test`, `save_test`, `system_test`, `registry_test` and `route_test`. They were
|
||||
written before a generic call inside a `test` body resolved and before each test had a fresh
|
||||
state, so the queue cases are functions a test calls and the runner's cases start with
|
||||
`core_clear()`; neither is needed any more.
|
||||
|
||||
## Later
|
||||
|
||||
- `port FishingWorld { ... }` / `bind` will replace the record of function values, checked at
|
||||
compile time instead of a null at run time.
|
||||
- `open registry Systems of System` could replace `core_add`: each mechanic `def`s its system and
|
||||
the game's own `def`s give the order. Function values in a `def` work today (`def Systems a {
|
||||
tick: fn a_tick }`); what is missing is a registry a package declares and a game fills, in an
|
||||
order the game controls. Until then the list is built by calls, in one place.
|
||||
- `Systems` is an `open registry`, so a system can be declared instead of added: `def Systems
|
||||
fishing { phase: PH_SIMULATE, tick: fn fishing_tick }` from any module. The runner starts from
|
||||
the declared systems - the order the compiler gives an open registry: ludic.base's own (none),
|
||||
then each other module's by module name, each module's in the order it is read - and `core_add`
|
||||
appends after them. A game that wants to decide the order itself writes the `def`s in its own
|
||||
files, or keeps calling `core_add` in one place.
|
||||
- `module toy_fishing uses ludic_base` will make rule 1 a compile error rather than a review.
|
||||
|
|
|
|||
|
|
@ -12,10 +12,17 @@ export property System {
|
|||
load: fn(Val, int) -> void = null
|
||||
}
|
||||
|
||||
# the declared list: a mechanic or the game writes `def Systems fishing { phase: PH_ACT, tick: fn
|
||||
# fishing_tick }` from its own module; the runner starts from these, then core_add appends
|
||||
export open registry Systems of System as SYS
|
||||
|
||||
var cs_list: []System = null
|
||||
|
||||
function cs_all() -> []System {
|
||||
if cs_list == null { cs_list = new []System }
|
||||
if cs_list == null {
|
||||
cs_list = new []System
|
||||
for i in 0 .. SYS_COUNT { push(cs_list, Systems[i]) }
|
||||
}
|
||||
return cs_list
|
||||
}
|
||||
|
||||
|
|
|
|||
21
packages/ludic.base/tests/registry_test.ludic
Normal file
21
packages/ludic.base/tests/registry_test.ludic
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# registry_test.ludic - systems declared with `def Systems ...` from outside the package: the
|
||||
# runner starts from them in the registry's order, and core_add still appends after them
|
||||
import "ludic.base"
|
||||
program RegistryTest {
|
||||
numbers float
|
||||
var trail: string = ""
|
||||
function late_tick(t: Tick) -> void { trail = trail + "l" }
|
||||
function early_tick(t: Tick) -> void { trail = trail + "e" }
|
||||
function added_tick(t: Tick) -> void { trail = trail + "a" }
|
||||
def Systems late { phase: PH_RESOLVE, tick: fn late_tick }
|
||||
def Systems early { phase: PH_INPUT, tick: fn early_tick }
|
||||
test "the declared systems run, phase by phase" {
|
||||
expect_eq(core_count(), 2)
|
||||
expect(Systems[SYS_LATE].key == "late")
|
||||
let s = system_new("added", PH_INPUT)
|
||||
s.tick = fn added_tick
|
||||
core_add(s)
|
||||
core_tick_all(tick_new(0.016, 1, 0.0))
|
||||
expect(trail == "eal")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue