Commit graph

1049 commits

Author SHA1 Message Date
2c800ea84d Merge commit 'ec49207' into lang/foundations (deps: roots out of the reach ratchet); the seed regenerated from the merged compiler, bootstrap-cfree fixpoint holds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:56:57 +03:00
4fb1dc0ddc Json.parse decodes an escaped string in one pass into one buffer - joined a character at a time, every shorter copy was kept, and a long escaped text took gigabytes (ui-preview: 6.5 GB in 3 s); protocol-v1.md states the @import path rule and the key a file override answers to
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:53:57 +03:00
ec49207533 ludic deps: widest_reach and widest_write_reach leave out the ROOTS - a function that reads fn values out of a table (a step list's walker, a registry of systems) reaches every state by definition - and count every other function through its calls only, not through a dispatcher nor a fn value it writes into a list; the roots are listed on a line of their own and marked in --reach / --wreach; reseeded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:51:50 +03:00
ea51307eaa Merge commit 'b053bf6' into lang/foundations 2026-09-29 21:47:39 +03:00
49e32f9b47 chunked tables: a key is unique across its map and the slot's own, not interned - a slot keeps row i's key in its own buffer i, rewritten when the slot is refilled (interning ~92k map-unique keys as a player walks would fill the bounded intern table and keep them all); ludicc --check refuses a key written in two of a map's chunk files, naming both; reseeded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:43:20 +03:00
b053bf625c ludic.things: a Thing's dawn (the last day a restock reached it, saved); things_restock takes the day; things_catch_up restocks a chunk's Things that missed a dawn once a morning would reach them, as that morning would have
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:36:28 +03:00
0634116d59 Merge commit '7c25c48' into lang/foundations 2026-09-29 21:31:11 +03:00
7c25c48dd2 render3d: a ground layer's instances each their own size (scale_var) and larger by band (band_grow)
GroundFill gains scale_var (an instance's scale x (1 +- var), from its own hash) and band_grow
(x (1 + grow * band)): a painted density carries the local mean, and without them every flower of a
kind was the same size and the far clumps lost the growth that kept the cover as the step widened.
Both 0: as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:29:58 +03:00
d8baf4e579 tests: the full suite's seven failures on lang/foundations - one compiler fault and six stale expectations
- the test runner re-makes every state between tests again: since 12fdc07 a state is made by its getter on
  first use, so re-running L_init_globals left the last test's state in place (state/tested failed its
  second test); @L_reset_states forgets every lazy state, and the runner calls it before each test
- diag_json_case counts errors, and an absent @Ref / @Tint / @OneOf target is a warning since d82dc31:
  ref_unknown is 1 error and node_bad 8
- schema_hash.ludic prints 1: a bool is 1 or 0 as text (random_plain's 1 1 1)
- permap_check_case looks for the unit warning without the quotes the JSON escapes
- baked_test's inputs-hash test makes its directory: each test has a temp directory of its own
- ludic deps prints phase 25's five counters too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:19:00 +03:00
9186abbbe3 render3d: the GPU blades grow by a painted density (a layer of the ground densities, grass_density_layer) where a map has one - a 5x5-tile window round the camera in a storage buffer the cull samples - and by today's rules where not; grass_rule_at is the rules on the CPU for the migration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:14:22 +03:00
9ff219d065 Merge commit '4376ddf' into lang/foundations 2026-09-29 21:13:56 +03:00
4376ddf0ec Json.parse decodes \uXXXX to UTF-8 - a surrogate pair joined, a lone surrogate or bad hex as U+FFFD - and \t \r \b \f, where it dropped the backslash and kept the hex as text; examples/lang/json_unicode checks it byte by byte
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:07:10 +03:00
e6f4685bd0 docs: map-scoped tables in LANGUAGE.md, the maps manifest key, a changeset
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:03:56 +03:00
5a751434ac tests: the map-scoped tables' example and rejected fixtures
examples/lang/permap (two maps, a chunked table with a negative, a positive and a
missing chunk, constants and fn by name, a nested list, a cross-row @Ref, a reload
shrinking in place, a bad file's file:line:col; built under arena strict with an @On
frame root), --check fixtures for a wrong field, a wrong type, an unknown constant and
a dangling cross-row @Ref, @Ref(PerMap) on an int, as PREFIX, and a @Unit warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:03:56 +03:00
2f4860ae71 @PerMap and @Chunked(n) registries: rows read per map, a state and verbs written, every map checked
- @PerMap registry R of T from "file.lres" reads <maps root>/<map>/file.lres at run time
  (package.ludic's new `maps` line, default assets/maps); @Chunked(n) one file per chunk,
  {cx}/{cz} in its name. No as PREFIX, no constants, no def, never open.
- permap_gen: state R, r_load/_clear/_find/_path; chunked: RChunk, r_in/_out/_slot/_find/
  _clear/_path; a typed reset and fill per record over lres.ludic, rows, lists and list
  records pooled per table / chunk slot, @alloc_ok on what grows at high water.
- permap_consts: lres_consts__(), the program's int and float constants by name.
- permap_check: ludicc --check reads every map directory (fields, types, constants, fn,
  @OneOf/@Range/@Ref, cross-row @Ref keys in the same map, a key twice); --no-maps skips.
- @Ref(PerMapTable) is refused on a non-string field; the schema says scope/chunk per
  registry and scope map on such a Ref, and lists the canonical @Unit spellings; any other
  @Unit spelling is a warning naming the canonical one.
- reseeded (mac + win seeds; bootstrap-cfree out.ll == seed.ll).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:03:56 +03:00
7170c31304 runtime: lres.ludic, the .lres reader behind map-scoped tables
A flat pooled tree (parallel lists reused from file to file), the file's bytes in a
buffer that grows only for a bigger file, strings unescaped into a kept scratch and
interned, the path built in a kept buffer, the program's constants by name (a sorted
table the compiler writes), and an open-addressing key hash rebuilt in place. Nothing
allocates past its high water; only an error's message is made, when a file is wrong.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:03:56 +03:00
313f95cd1f ludic.update: update_notes answers only for the language update_notes_for kept (none for another), and the tests follow the contract 3ac1179 set - the notes are kept by update_notes_for and whether this copy is installed is worked out when the updater is configured; they read the old per-call answers and failed 2 of 12
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:02:21 +03:00
359de7bfe5 Http.text and Http.header return copies - they handed back the handle's own buffer (and on macOS the response's string), which Http.free released: a text read before the free and used after it was garbage or empty (maroon-lake's maps.json was written with 0 bytes)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:57:44 +03:00
aba5d9e679 Merge commit 'cc24409' into lang/foundations 2026-09-29 20:55:24 +03:00
cc24409d87 ludic.wildlife: painted habitats and ranges through the port (habitat_in, range_at), the rule where none is painted
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:35:13 +03:00
d63de113fb Merge commit 'c672be1' into lang/foundations 2026-09-29 20:34:56 +03:00
c672be101e render3d: ground layers placed from painted densities - the density tiles (LGD1), their reader and the fill
ground_density_bake(path, key, version, hash, pngs) cuts each layer's PNG (R the chance a cell keeps one,
G an optional scale) into the terrain's 64 m tiles, one layer decoded and let go at a time, as a bake's
file; ground_density_open(...) reads it - or, when it is missing or stale, a dev build's own copy cut from
the PNGs - a tile at a time into a 256-tile clock, never a layer whole. ground_fill(s, cx, cz, band, g)
places a GroundFill row's instances in a stream chunk: a candidate per cell of the band's step, jittered,
kept when a hash of (layer, chunk, band, cell) falls under the density (times the game's trample,
r3d_on_ground_trample, where the row asks); scale, yaw, seed and wind from the same hash. Nothing calls
it yet: behaviour unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:33:51 +03:00
668a9ecf69 vehicles: a kind's numbers are a VehicleSpec the game hands over (vehicles_spec), not constants
The seat height, the horse's walk, gallop, acceleration and turn (VE_HORSE_*), its stamina (a gallop
over 6 m/s spends 6 a second, a walk gives back 3, 5 needed to gallop) and hay (0.02 a metre,
VEHICLE_HAY_MIN), the boat's stroke, turn and wind (VE_ROW, VE_ROW_TURN, VE_WIND) and where a rider
gets off (the horse's 1.2 m flank, the boat's 1.25 m wade) move into VehicleSpec (spec.ludic), held
per kind in VehiclesState.ve_specs and kept across a reset. vehicles_spec(kind, spec) sets one,
vehicle_spec(kind) asks it; a kind with no spec cannot be called. VEHICLE_HORSE and VEHICLE_BOAT stay
the kinds. The tests hand Maroon Lake's numbers in, and a new one holds a spec's walk and seat_h.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:22:02 +03:00
a87f20c66e ludic.anim: animset_choice names the playing choice's clip as the set does, animset_can says whether the rig carries any of a state's clips (a one-shot with none is not asked for)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:19:41 +03:00
a22b8a572b ludic.things: a Thing's seats (a run of the game's seat rows, saved; a bit per seat taken, not); ludic.character: char_sit takes the seat's top and its height above the feet, char_sit_leave names where standing up goes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:19:41 +03:00
e84a59aeff ludic.ui: a pooled node's reset lets go of its wrapped lines instead of clearing them - they are the memo's list (ly_wrap), shared by every node with that text, so a text that wraps vanished from the third frame on, in the game as in ui-preview; smoke.txt holds four frames of it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:17:39 +03:00
dd25267502 templates: {s} with one string hole and nothing else is a new string, not s itself - it was the one template that passed its string through (string() does), and code that wrote it as a copy kept what it then freed; the mark rides the callee, since escape analysis writes the call's uns; reseeded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:14:58 +03:00
a436cfbe35 ludic.ui: a rule's selector text is interned - {sel} was sel itself (a template of one string hole passes the string through) and lss_rule frees sel, so rules <id> named whatever reused the bytes; smoke.txt's tree order follows the document, and smoke.py runs the transcript
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:10:13 +03:00
e74656c372 Merge commit '49b5e80' into lang/foundations 2026-09-29 20:05:26 +03:00
49b5e80af5 ludic ui-preview: ludic.ui components previewed for a studio over stdio (R5)
A prebuilt tool, bin/ludic-ui-preview (built by ludic-dev build, shipped beside ludic-lsp, run as
`ludic ui-preview [--font DIR]`): ludic.ui with a backend that records every draw call as a line,
and mock component classes the studio describes (load / model / calls). frame t runs ui_show at
interface time t; text is measured on the CPU with the game's font.json metrics; locale goes
through ludic.i18n; tree / box / rules inspect the frame. No game code, no GPU, no network. The wire
protocol is frozen as tools/ui-preview/protocol-v1.md; smoke.txt is a transcript to run.

ludic.ui gains, all additive: UiClass.make_of, UiBackend.said / emitted, UiRule.text / at (with
comment line breaks kept so rule lines count true, and a class's styles read under its .lss path),
and ui_root, ui_find, ui_rules_of, ui_rule_value, ui_building, ui_class, ui_class_load,
ui_file_forget, ui_errors_clear; ui_nine_cuts_into exported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:53:26 +03:00
281ebc23e1 ludic schema: a components section and a natives list (R4)
Each UI component: module, place, doc, xml and lss paths, props and state
(type, default as written, place, doc), states_read (the header's states),
derived fields with their types, functions and events as the template calls
them (states and instance stripped), and the native tags its template uses.
natives: every ui_native / ui_native_input call with a literal tag - tag, via,
handler, place. schema_version stays 1; the lists are additions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:36:04 +03:00
28f7b4666b Merge commit 'e908672' into lang/foundations 2026-09-29 19:22:23 +03:00
e53a9fc865 Merge commit '3f27235' into lang/foundations 2026-09-29 19:22:23 +03:00
bda7489fbe Merge commit 'f66b912' into lang/foundations 2026-09-29 19:22:22 +03:00
abc78baad8 Merge commit '81cb992' into lang/foundations 2026-09-29 19:22:22 +03:00
3f2723582f render3d: an impostor's bake is BC7 with its mips - both atlases uploaded compressed, as baked
impostor_from_baked / impostor_refill read a bundle of two DX10 .dds (albedo, normal: BC7_UNORM, every
level) through tex_load_dds_at and gpu_tex_compressed - no gpu_tex_mips - and check each is the
impostor's size; sampled as a painted atlas is (clamped, no anisotropy). No BC on the GPU, or a bake
that does not fit, and the caller paints RGBA8 and makes mips as before; the fog re-open reads the BC7
file again. The handles and every draw are unchanged. Compile-only: nothing run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:18:48 +03:00
e908672034 render3d: a grass kind - the GPU blades' per-kind numbers are a GrassKind record the game hands over (grass_kind_set), not constants
GrassKind (grass_kind.ludic) carries what grass.ludic, grass_gpu.ludic, grass.vert, grass_cull.comp and
model.frag's BLADE path held as constants: the cell, s0 / d0 / radius, the row bands and rows, the blade
profile (neck, root, swell, tip, bend), the heights, clumps, widths and arch, where it grows (slope, snow,
the photograph's test), the root / tip / gone-to-seed colours, the far tufts, the root occlusion, the
roughness, the sheen and the wind. Its defaults are those constants exactly, so a game that sets nothing
draws as before. The shaders read them as u_gk_* (the cull as five more Params vec4s, 288 bytes);
grass_reset.comp writes each band's index count so a kind of other rows reaches the draw in order.
grass_quality holds the kind to a settings tier (never denser, never farther); R3D_GRASS_* still win.
grass_profile_w / _bend are the one profile, for the meshes and a game's preview. grass.mesh takes only
the cell: the rest of it is an older copy that already differs from grass.vert, left as it draws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:15:39 +03:00
f66b912cf4 Udp.open_local(port): a socket bound to 127.0.0.1 alone (udp.ll, udp_win.ll), for a port only this machine's tools reach - a dev link - where Udp.open binds every interface
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:15:30 +03:00
81cb992fad attributes: @Material(field) resolved as @Node is; @OneOf on a string field takes words, and every registry row's value is checked against them - words on another field, or constants on a string, is an error; reseeded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:13:32 +03:00
7f476513cd Merge commit 'd82dc31' into lang/foundations 2026-09-29 19:12:28 +03:00
d82dc3162f attributes: a target the program does not have at all (an @Ref registry, an @Tint / @OneOf constant) is a warning and "unresolved" in the schema, not an error - a package names the game's registry without importing it; a name of another kind is still an error; reseeded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:10:45 +03:00
6c690c5db6 render3d + lab: bakes as PNGs, and impostors, the sky's light and the carpet read from their bakes
ludic.lab: lab_png_write / lab_png_write_from (raw 8-bit, 1-4 channels, stored deflate) in png_write.ludic,
importable alone with its own LabPngState; png_convert.ludic's previews of float textures (R32F min..max,
RG16F x255, HDR x/(1+x) + sRGB); lab_ppm_to_png on the same encoder.
render3d: bake_load.ludic - impostor_from_baked / impostor_source / impostor_refill (a fog re-open reads
the bake), sky_baked_in and sky_precompute trying the bake at the start yaw (sky_compute is the
convolution, and sky_ibl_bytes always uses it), carpet_from_baked / carpet_bytes / carpet_finish,
bake_part_count / _len / _off. Compile-only: nothing run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:10:06 +03:00
18755d9057 ludic.devlink: an editor's live link into a running dev build (protocol v1, stage 1)
A package on ludic.base alone. DevlinkNet is the transport (a game binds Udp, the test a fake);
DevlinkWorld's members are the verbs, each defaulting to "not offered" (err unbound): ping, hello (with
Build.schema_hash as 16 hex digits), cam_get / cam_set / cam_release, goto, map_load, time, weather, shot,
pause / resume / step. One request a frame, parsed in place from a fixed 8 KB buffer and answered into
another; map_load, shot and step answer later by id, one at a time, 5 s at most. Loopback senders only,
opened only when enabled() (dev_tools), co-op refuses everything but ping and hello. tests/devlink_test:
each answer byte for byte, bad arguments, a stranger dropped, co-op, the late answers and their timeout,
a closed build that never opens. Compiles; not run (the Master runs the package tests).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:09:59 +03:00
dd1e852356 Merge commit 'dacc0f7' into lang/foundations 2026-09-29 19:06:20 +03:00
dacc0f7280 Build.schema_hash(): FNV-1a 64 of the program's own schema (the bytes ludic schema prints), worked out only when a program names it, 0 under ludicc --release, which ludic bundle now passes (Mac and Windows); reseeded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:05:28 +03:00
5517873275 ludic.anim: animation sets - the game's src/animset moved in as it stood (records, AnimPlayer, animset_bind / request / param / tick and the accessors); AnimSets is an open @ByKey registry a game fills with def AnimSets from; a test on a rig of hand-made clips
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:01:15 +03:00
d9a73d63bd ludic.base: an input that is itself a bake hashes by its payload, not its header - a re-bake that makes the same bytes under a new inputs hash leaves the bakes that read it current
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:56:50 +03:00
8acce7beb9 Merge commit 'f793908' into lang/foundations 2026-09-29 18:54:29 +03:00
f79390838a render3d: a glTF material's factors are drawn - base colour, roughness, metallic and emission
gltf_factors reads pbrMetallicRoughness.baseColorFactor, metallicFactor, roughnessFactor and the
emissiveFactor into the primitive (pr.fac), and prim_factors hands them to every program that draws its
textures (actors, the scatter's meshes and levels, the impostor bake) as 1 - factor, so a program never
given them - or a material that gives none (pr.fac null) - multiplies by 1 and draws as before. An
untextured material with a colour (or a metal/roughness) samples a pure white for it, so the factor is
exactly what it draws: horse_cornea is its own colour, not the 200 grey every untextured part had.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:52:42 +03:00
e8d766eafb Merge commit 'b5bffe9' into lang/foundations 2026-09-29 18:51:40 +03:00