Skip to content

Projects, bundles, releases

This page answers the question that comes back every week: what is a bundle, where does what live, and what goes to the board?

The vocabulary, in one table

Word What it is
Project the game being written: its .lua files (main.lua, always) and its .mask files
RAW library the images loaded as they are, in true colour (RGB/RGBA): tilesets, sprites, animated sprites
Bundle a series of assets with its palette computed over the whole set, and its compatible variants (same indices, other colours: day/night). It is the only indexed form of the art. A game can have several
Attachment "this project uses bundle B, seen by the game under the name G", a reference, not a copy
Group (console side) what the board sees of a bundle: G.pal, G.atlas, its indexed PNGs, its variants G.<v>.pal, its backgrounds G-<f>.map + G-<f>-tiles.png
Release the published snapshot: a complete folder, the files of the project plus the pieces of its attached bundles, frozen, identified by the fingerprint of its manifest

So: a bundle of the App = a group on the console, and the "name seen by the game" of the attachment is the name of the group: gfx.palette("G"), gfx.id("G/perso"). This name follows the rule of groups: lowercase, digits, underscore, no hyphen (the hyphen separates a group from its background).

The firmware says "the game folder" for the whole that gfx.bundle() compiles in one go, all the groups. That is the same object as the release here, not a bundle of the App.

A game is a flat folder

A game is a flat folder of .lua, of .mask and of the pieces of its bundles, nothing else. Flat because require() is flat on the console: the same rules on both sides, so that a project that works online works on the board.

It is identified by (account, name) and never by the nickname, which can change.

Bytes are addressed by their fingerprint

A file is stored under the fingerprint of its content, never under a project path. Two identical files, the normal case from the first clone on, therefore share a single object: cloning a game copies no bytes, only lines.

What goes where, and why

  • .lua and .mask in the project. The .mask is the collision geometry of the level, hand-drawn in ASCII: it is code, not art.
  • .map in the bundle. The tile map of a background means nothing outside its tileset; the two are one piece of the group (G-<f>.map + G-<f>-tiles.png). A .map that arrived with an imported example is tolerated as a project file.
  • One palette per bundle, computed over all its assets. An indexed pixel only makes sense in the palette that produced it: that is why an asset is never quantised alone, and why the console only displays sprites of one group at a time (a single active palette).
  • Publishing materialises. A release must sit as it is on a micro-SD card; it therefore embeds the pieces of the attached bundles, named after the group. The .zip export of the draft does the same. The project, for its part, keeps only the reference: retouching a bundle changes the next publication, not the previous one.
  • An asset used by a bundle cannot be deleted, nor can a bundle attached to a project. The App refuses, because otherwise a game would stop loading far from the cause.

Names, and their bound

A full name, <group>/<region> for gfx.id(), <group>-<background> for gfx.background(), has to fit in the 48-byte buffers of the console and of the simulator: 47 characters at most. This name does not exist until the group (the name seen by the game, chosen at attachment) is chosen; the sum is therefore checked at the two moments where it can be born, at attachment, and at the recalculation of a bundle already attached, with a refusal that names the culprit. Each half keeps its own rule (60 at most), and no pair that fits is forbidden.