ludic/packages
Orkuncakilkaya b368912c78 nav, physics: Recast's and Detour's memory counted (nav_heap_bytes / _peak / _allocs, a counted allocator registered at load, tile bytes staged from it); package tests that crossing the map and back holds the native bytes
The user's walk showed the footprint rising outside the Ludic heap. Jolt's bytes were already counted;
the navmesh's were not, so nothing could see them. Now:
- ludic.nav nav_heap_test: fifty crossings of a four-tile map by the resident index hold the tiles in and
  the native bytes to the first crossing's; a thousand crowd walkers in and out grow nothing; a reset
  gives back every byte the mesh took.
- ludic.physics cross_heap_test: a 1 km map of 64 m chunks kept to a ring round a player crossing
  corner to corner and back, each chunk a heightfield, twelve owned posts and six owned scaled hulls
  as the game makes them: six more crossings hold the bodies, the shapes and Jolt's bytes and peak
  exactly. (A post made as an offset of a cylinder, with only the offset owned, leaked the cylinder
  every time: the game's solid_pillar owns its cylinder directly.)

lib/macos-arm64 rebuilt; lib/windows-x64 needs native/build.sh run on the PC for the new exports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:32:18 +03:00
..
ludic.aim ludic.character: the look goes up and down to 89 degrees (was -55..35), and the orbit's heading holds when the camera is straight over or under the pivot; ludic.aim: aim_ahead, whether a point is before the view 2026-09-29 12:14:04 +03:00
ludic.anim frame allocs (25.2): ludic.anim's pose scratch made with its state (pose_part, the name no longer taken for udp_ip's part), a model's clips read at its load declared 2026-09-28 16:29:15 +03:00
ludic.audio audio: a volume per channel under the master (effects, wildlife, ambient, interface) - aud_channel_set / aud_emit_on, aud_ui on the interface channel, and Audio.music_volume for the ambience loop on the runtime's music channel 2026-09-29 12:11:29 +03:00
ludic.base ludic.base: sb_stdout - a StrBuf's bytes to standard output with no string made (a per-frame log line the fence would otherwise see kept) 2026-09-28 22:58:37 +03:00
ludic.character character: a seated body is held by its seat alone - no step to the walker while sitting, and a hold no longer turns it off the seat's heading 2026-09-29 13:12:22 +03:00
ludic.clock ludic.fishing, net, clock: their dice made once and seeded again, never made again per reset; ludic.minimap's near list kept, its marks and grid declared 2026-09-28 20:13:58 +03:00
ludic.compass @max on the bounded lists: TextRing's slots, the pack's counts and change ring, the net's fact ring, the compass pools, ludic.ui's memo (its 3/4 of MM_CAP), screen pools, answer and pointer rings; ludic.base's intern test holds the table to its cap now that past it is Mem.over 2026-09-28 16:48:53 +03:00
ludic.core feat(engine): #90 atlas-aware Sprite component, #91 become from listeners, 0.3.x ergonomics batch 2026-09-04 01:36:08 +03:00
ludic.crafting ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.dungeon wip(0.S3): outside the runtime, its drawable size, scale, screen and tile size are read and set through its functions; a migration reports such a reference instead of threading the runtime's state 2026-09-25 15:47:21 +03:00
ludic.effects Merge branch 'lang/memory-burn3' into lang/foundations 2026-09-28 20:13:59 +03:00
ludic.fire ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.fishing ludic.fishing, net, clock: their dice made once and seeded again, never made again per reset; ludic.minimap's near list kept, its marks and grid declared 2026-09-28 20:13:58 +03:00
ludic.gameplay refactor(packages): 0.R5 - the rest of the lazy starts are defaults - hints' rail (sized by hints_config), lab's CRC table, rpg's crafting / items / inventory / quests / dialog, shooter's weapons, prefs, and npc's and gameplay's empty ensures gone; gameplay's faction table sits beside its state, since a default names only what is declared before it; --tighten over their tests and examples 2026-09-27 03:07:29 +03:00
ludic.gear ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.hints ludic.settings: the store filled into a kept object in place; texts read from a file kept apart from it 2026-09-29 12:22:07 +03:00
ludic.i18n ludic.i18n: a language's lookups kept once read; switching back restores them 2026-09-29 12:21:53 +03:00
ludic.inventory ludic.ui: a select matches a number as a number (its text was made every frame per select); the fired-event queue's lists made with room; ludic.inventory: the counts made at full length at once 2026-09-28 21:09:08 +03:00
ludic.jobs fix(ludic.jobs): jobs_taken and jobs_shown fill a kept list per group (the map and the journal asked every frame, and the fence caught a list kept) 2026-09-28 23:01:53 +03:00
ludic.lab feat(render3d): 23.3 - a .dds beside a .png is uploaded BC-compressed with its whole mip chain 2026-09-28 01:23:45 +03:00
ludic.minimap ludic.minimap: minimap_seen_text writes the explored grid into bytes the state keeps (a string per cell before, every save) 2026-09-28 22:33:49 +03:00
ludic.nativeecho feat(pkg): windows-x64 builds of ludic.physics (Jolt v5.6.0) and ludic.nativeecho, built on the PC by their native/build.sh 2026-09-27 13:29:55 +03:00
ludic.nav nav, physics: Recast's and Detour's memory counted (nav_heap_bytes / _peak / _allocs, a counted allocator registered at load, tile bytes staged from it); package tests that crossing the map and back holds the native bytes 2026-09-29 13:32:18 +03:00
ludic.needs ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.net ludic.fishing, net, clock: their dice made once and seeded again, never made again per reset; ludic.minimap's near list kept, its marks and grid declared 2026-09-28 20:13:58 +03:00
ludic.npc capacities (25.5a): @max on Physics' bounded lists - every fact pool and free list (1024), the walker slots (64), the shape and body-owner tables (262144), npc's walkers and posts (64) 2026-09-28 16:49:58 +03:00
ludic.npcai refactor(runtime,packages,examples): named constants, package enums, idiom sweep 2026-09-05 01:12:26 +03:00
ludic.photo photo: a frame's tags written into a kept buffer and interned, one string per distinct text - a viewfinder composes every frame, and each tag concatenated a new text the frame dropped 2026-09-29 13:29:00 +03:00
ludic.physics nav, physics: Recast's and Detour's memory counted (nav_heap_bytes / _peak / _allocs, a counted allocator registered at load, tile bytes staged from it); package tests that crossing the map and back holds the native bytes 2026-09-29 13:32:18 +03:00
ludic.platformer refactor(runtime,packages,examples): named constants, package enums, idiom sweep 2026-09-05 01:12:26 +03:00
ludic.prefs refactor(packages): 0.R5 - the rest of the lazy starts are defaults - hints' rail (sized by hints_config), lab's CRC table, rpg's crafting / items / inventory / quests / dialog, shooter's weapons, prefs, and npc's and gameplay's empty ensures gone; gameplay's faction table sits beside its state, since a default names only what is declared before it; --tighten over their tests and examples 2026-09-27 03:07:29 +03:00
ludic.render3d render3d/shadow: tree impostors cast only into the far cascades 2026-09-29 12:37:24 +03:00
ludic.rpg refactor(packages): 0.R5 - the rest of the lazy starts are defaults - hints' rail (sized by hints_config), lab's CRC table, rpg's crafting / items / inventory / quests / dialog, shooter's weapons, prefs, and npc's and gameplay's empty ensures gone; gameplay's faction table sits beside its state, since a default names only what is declared before it; --tighten over their tests and examples 2026-09-27 03:07:29 +03:00
ludic.save ludic.save save_write_tree: a tree straight to disk through Json.write_file, the backup and read-back kept; ludic.telemetry writes its id file the same way 2026-09-28 15:42:59 +03:00
ludic.session ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.settings ludic.settings: the store filled into a kept object in place; texts read from a file kept apart from it 2026-09-29 12:22:07 +03:00
ludic.shooter refactor(packages): 0.R5 - the rest of the lazy starts are defaults - hints' rail (sized by hints_config), lab's CRC table, rpg's crafting / items / inventory / quests / dialog, shooter's weapons, prefs, and npc's and gameplay's empty ensures gone; gameplay's faction table sits beside its state, since a default names only what is declared before it; --tighten over their tests and examples 2026-09-27 03:07:29 +03:00
ludic.shop ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.steps Merge branch 'lang/memory-burn3' into lang/foundations 2026-09-28 20:13:59 +03:00
ludic.telemetry fix(allocs): ludic.update, steps, effects and telemetry at 0 frame allocs and 0 keeps 2026-09-28 20:13:06 +03:00
ludic.things ludic.compass, inventory, shop, base, things: the compass's lists are state defaults and push through kept_push; the pack clears in place; setup, bind and load paths say so in @alloc_ok 2026-09-28 16:29:33 +03:00
ludic.tracks ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.ui ludic.ui: the pointer looks through a box with no size; a control's parts answer :hover 2026-09-29 13:26:33 +03:00
ludic.update frame allocs: a local shadowing a function is no edge, the drain's calls are no edges, HEAP keeps only a growth off the arena, and the arena starts at its first use 2026-09-28 20:23:28 +03:00
ludic.vehicles vehicles: a boat nobody rows is moored where it was called or left - its hull put back on the mooring (home_x, home_z, home_yaw) whenever it strays, and no wind on it; the wind drifts only a rowed boat 2026-09-29 12:12:44 +03:00
ludic.wallet ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.weather ludic.base: a queue's fact records come round again (q_rec / q_ring_add), used by effects, clock, wallet, weather, tracks, needs, fire, settings, update, steps, crafting, shop, gear, hints, session and photo instead of a new record per fact; the minimap's marks cleared and rubbed out in place, its scale steps without a list; the clock's dice seeded again, not made again; once-per-join, per-save and pool-miss paths declared 2026-09-28 20:03:46 +03:00
ludic.wildlife ludic.wildlife: wildlife_reserve sizes both animal tables and their id maps at a world's start 2026-09-28 21:45:55 +03:00
README.md feat(ludic.anim): ozz-animation 0.17.0 underneath - a skin's skeleton and each clip built at load, sampled to 1e-4 of anim_mix (phase 19.1) 2026-09-28 12:44:49 +03:00

packages

The Ludic packages that ship with the toolchain, found by import "ludic.<name>" from $LUDIC_HOME/packages (or a project's ludic_modules). Each has a README of its own. ludic test packages runs every package's tests, each test block in a process of its own.

The mechanic packages

A mechanic says module ludic_<name> uses ludic_base and so reaches nothing else; it asks the game through ports (bind ClockWorld { ... }), reports through queues of facts, and saves its own section. The rules are in ludic.base.

ludic.base the shared vocabulary: Tick and phases, Queue<T>, rng streams, the save tree, the system runner (Systems, an open registry)
ludic.aim what the crosshair is on: a forgiving ray against cylinders, the ground in front, reach floors, which thing the use key means
ludic.anim glTF animation clips sampled and cross-faded onto a render3d Skin (uses ludic_render3d); ozz-animation underneath as a native library (phase 19)
ludic.character a third-person walking body: gait, steps and slopes, a jump, wading, swimming, sitting, a safe put-down, the orbit camera
ludic.audio world sounds with a gain, pan and pitch from a listener port, flat interface sounds, packed clips loaded, the one door to Audio.*
ludic.compass bearing marks on a strip: gathered from registered providers, gated by tier, sorted by priority, bearings, distances and a radar
ludic.clock the hour, the day, the moon and a calendar of seasons
ludic.crafting recipes: what goes in and out, where and how long, making, refunding, hold-to-make
ludic.effects timed modifiers that run down in game time (a meal's warmth, a drink's legs)
ludic.fire a camp fire: fuel, the rain on it, warmth at a distance, whether it will cook
ludic.fishing a rod at the water: cast, bite, the reel's fight, the landing; species as an open registry
ludic.hints a rail of what is true now, most urgent first, each a card that teaches it and can be muted by key
ludic.i18n a game in any language: gettext .po files, patterns with holes, plurals, a mod folder, a font per language
ludic.inventory a pack: a count per kind, with room the game decides
ludic.minimap a map's logic: the explored fog over a grid and its land share, the player's marks, and the view (world to map and back, zoom, pan, the camp, you, focus)
ludic.needs a body's warmth, food, water and energy, and the countdown to a collapse
ludic.npc the other people in a place: a routine by the hour, walking round what is in the way, facing a player who comes near, lines as data, a guest's copy
ludic.physics rigid bodies, removable still shapes, queries and buoyancy over Jolt Physics (a native library, phase 16)
ludic.nav a navmesh per kind of walker and the ways across it, over Recast & Detour (a native library, phase 17)
ludic.photo a camera's photographs: what is in the frame, a grade on size, framing, light and the moment, the roll, its worth, the best of each subject
ludic.save versioned save files: a migration chain the game declares, torn writes told apart, a backup, a newer file refused and read-only
ludic.settings a game's settings as data: one store, a fact per change, ranges, a safe set
ludic.shop vendors: prices by standing and weekly demand, stock, buying and selling
ludic.steps chapters of steps (kind, param, need), progress, and a party's shares pooled
ludic.telemetry events queued, batched to a PostHog-shaped endpoint, backed off when offline
ludic.things the placed things of a world and their kinds (an open registry), queries, facts, a save
ludic.tracks prints and sign animals leave, their age, and reading them
ludic.update a Velopack-installed game updating itself a frame at a time
ludic.vehicles each player's boat and horse: called to berths and bays, owner and rider, riding, getting off onto a safe spot
ludic.wallet money carried: earned, spent, lost, every change a fact
ludic.weather spells of weather described as data: odds, wetness, wind, storms, a forecast
ludic.wildlife a valley's animals: ranges, their day, senses and alert, feeding, lures, spawning

Engine and UI

ludic.render3d the physically based 3D renderer over OpenGL and Vulkan (named ludic_render3d under uses)
ludic.ui components: templates, stylesheets, layout, input and focus (module ludic_ui)
ludic.lab the visual lab: a scene on a lit plate, fixed cameras, PNG shots and a contact sheet
ludic.core the canonical engine-ABI components
ludic.prefs a small key=value store for preferences and records
ludic.nativeecho the worked example of a package carrying a native library: a C sum over a slice, callbacks drained as facts

Native libraries

A package may carry a C or C++ library (native lines in its package.ludic, docs/PACKAGES.md). The library is never bound directly: a thin C shim in the package's native/shim/ is, and every shim keeps the same shape so the Ludic side stays safe.

  • Only int, long, float and opaque handles cross. A Ludic float is a C float, a long an int64_t, a pointer a void *. No struct is passed by value, no C++ type, no exception: the shim catches and turns it into a return code.
  • A handle is the library's pointer, kept in the package's state and never exported. The game names what the package hands it - an int id - and the package maps it to the handle.
  • Nothing calls back into Ludic. No function pointer crosses the boundary. What a library reports through a callback (a contact, a finished path, a mixed buffer) the shim records in a buffer of its own, and the package drains it after the call returns (ne_scan then ne_drain), pushing each record onto its facts queue. A record that did not fit is counted, not dropped silently.
  • Bulk data crosses as a slice the package owns. A []float or []int passed where the shim takes a pointer is its data; the package makes it once and reuses it (L7), and passes its length beside it.
  • Errors are return codes. 0 is success and a negative number says what failed; nothing aborts, nothing prints.
  • Threads a library starts stay inside C (a job system, an audio device). The Ludic side is called on the game's thread only.
  • The symbols are prefixed with the package's short name (ne_, jph_), exported explicitly (__attribute__((visibility("default"))), __declspec(dllexport)), and everything else is hidden.

native/build.sh builds lib/<target>/ from the pinned upstream source and the shim through tools/native/lib.sh; run it on the Mac and, in Git Bash, on the PC. The package's tests run the real library - there is no fake for it.

The builtin controllers

ludic.gameplay the shared foundation of the builtin gameplay controllers
ludic.platformer the Platformer controller
ludic.shooter the top-down Shooter controller
ludic.rpg the RPG systems suite
ludic.dungeon arena rooms for a room-to-room roguelite
ludic.npcai NPC AI: perception, decision, action