In this guide
Install reusable gameplay modules and assets from the free catalog.
Packages
Packages are reusable, versioned building blocks: Lua gameplay modules and/or assets. Everything in the DemiEngine package catalog is free. Games install packages into the project; the game never contacts a registry at runtime.
What a package contains
{
"format_version": 1,
"name": "demi.gameplay.health",
"version": "1.1.0",
"engine": ">=0.1.0",
"dependencies": { "demi.gameplay.events": "^1.0.0" },
"public_modules": ["demi.gameplay.health"],
"files": [
"README.md",
"scripts/demi/gameplay/health.lua",
"tests/health_test.lua"
]
}
- Package name is the installation identity.
- Public modules are the import surface — one package can export several.
- Files (with
asset_manifestsandengine_extensionssubsets) are the audited content list. - Never import another package's private module path.
Install packages
cd games/my_game
demi package add demi.gameplay.health@1.1.0
demi package add kenney.textures.prototype@1.0.0
demi package install
demi package list
demi package outdated
Project packages holds constraints; demi.packages.lock.json pins resolved versions. Installs land in .demi/packages (derived data — do not commit or hand-edit).
Cook reads only the lockfile and .demi/packages. Package asset IDs merge into the shared registry without copying.
Use package modules
local GameplayEvents = require("demi.gameplay.events")
local Health = require("demi.gameplay.health")
local health = Health.new(GameplayEvents.new())
health:add("player", 100)
health:damage({ target = "player", amount = 15 })
print(health:get("player").current)
Installed package modules follow the same explicit-require rules as engine services. Health.new takes the events bus as its first argument, so gameplay systems can subscribe to damage_applied, health_changed, and entity_defeated. Editor stubs come from the package's public module metadata.
Available package families
| Family | Modules |
|---|---|
| Gameplay | demi.gameplay.events, health, controllers, projectiles, interactions, inventory, needs, encounters, camera, checkpoints, third_person, destruction |
| UI | demi.ui.localization |
| Networking | demi.network.lobby |
| Assets | kenney.textures.prototype — 78 CC0 prototype textures |
Browse descriptions, versions, and install commands on each package page in the catalog.
Test a package
demi package test packages/my_package
demi package test packages/my_package --format json
Declared Lua tests run in the same runtime used by games.
Publish a package
- List the package's files, including its root
README.md, indemi.package.json. Run its tests before publishing. - Build a local registry archive:
demi package publish path/to/package --registry /path/to/staging-registry. The archive is written belowpackages/<name>/<version>/package.demipkgin that registry. - Upload the archive and listing metadata using the publishing page or
python3 tools/package-store/bin/publish.py package.demipkg metadata.json. Publishing requires the private registry key.
Releases are immutable. Listing metadata (title, description, tags, images) can be updated later without touching archive bytes.
The package overview displays the README bundled in the published archive. Deploying the website or editing a repository README does not update a published package. Bump the package version and publish a new archive when its code, manifest, or README changes. Publish dependencies before packages that require them.
Design rules
- Gameplay rules belong in Lua or reusable packages, so the engine stays a general mechanism.
- Package dependencies use manifests and locks with declared visibility.
- Keep module names stable across releases so games do not need rewrites on every update.
- When a public API changes, update examples, docs, tests, and stubs together.
Reusable character needs
Character Needs supplies named bounded values, time-based drain/recovery rates and separate low/recovery thresholds. It works without scene, physics or HUD dependencies. Games decide when to rest, what food costs and what happens on depletion. The Colony Builder example uses it for stamina, nutrition and suit oxygen.
local Needs = require("demi.gameplay.needs")
local needs = Needs.new({
stamina = { value = 80, low = 25, recovered = 90 },
})
needs:update(dt, { stamina = -0.4 })
if needs:urgent() then
-- Ask your game's task scheduler for a break.
end