The harness: modules around a game
A game needs things around it to run: a CPU with the right features, an operating system, its graphics and sound APIs, its libraries, and its DRM or store client. Each project used to find these one failure at a time. The harness writes each answer down once, as a module, so the next game that needs the same thing starts with it.
There are three parts:
- The analyser,
tools/triage/needs.py, reads an executable, a game's folder or a disc image. It lists the game's needs askind:valuekeys with their evidence:api:glide2becauseGAME.EXE imports glide2x.dll,drm:steambecause ofsteam_api64.dll,os:windows-3xfrom an NE header. - The modules, in
docs/harness/modules/here and in a project's owndocs/harness/modules/. A module says which needs it provides, which runtime it works in, which modules it needs first, how to set it up, and how to check it works. - The loader,
tools/live/harness.py. It runs the analyser, picks a runtime module that covers the most needs, adds a layer module for each need left, and writes the plan. With--scriptit writes the set-up steps as one shell script for you to read and run.
./gameframe run needs game/ # what the game needs
./gameframe run harness plan game/ # which modules cover it, and what is missing
./gameframe run harness plan game/ --runtime emupc-wine --with silent-audio --script derived/harness.sh
./gameframe run harness modules # every moduleNeeds
A need is a kind and a value. The kinds are cpu, os, extender, api, lib, drm, store and media. needs_signs.py beside the analyser holds every sign it reads and the need each sign shows. A need is a lead and never a proof. An import shows the program can use an API, and a build may never call it. Confirm a need by running the game.
Modules
A module is one Markdown file. Its front matter uses the same small YAML subset as the know-how entries:
| Field | What |
|---|---|
id | the file name without .md |
title | one line: what the module gives the game |
kind | runtime (what the game runs in: Wine, an emulated PC, DOSBox-X) or layer (something added inside a runtime) |
runtime | for a layer, the runtimes it works in; for a runtime, its own id |
provides | the needs it covers |
requires | the modules it needs first |
vars | the variables its steps read, such as STEAM_APP_ID |
seen | the games it was proved on, with the date |
The body has these sections:
- What it does: one paragraph.
- Set up: the steps, as
bashblocks. The loader copies these blocks into the script, in order. - Check: how to see that it works.
- Pitfalls: what went wrong, and what each symptom means.
The steps must run without a person, except where a person must act, such as signing in to a store. Mark those steps, and never type the owner's credentials.
Learning modules from games
When a game starts to run because of a change around it, write that change as a module. Copy modules/_template.md. Put a module that only this game needs in the project's docs/harness/modules/. Put one that any game with the same need could use in GameFrame's (AGENTS.md rule 8). Add the game to seen each time a module works for another game. When a module fails for a game, add the symptom to its Pitfalls, or write a know-how entry (gameframe advise).
Vision
When a step needs to find something on the screen, such as a button to click in a VM, tools/live/align.py lines up a reference picture with a capture of the screen and maps points between them. When the screen has changed too much to line up, tools/llm/locate.py asks a model to find the element, on API credits (API credits).