Skip to content

@opys/runtime ​

npm

The part of opys a launcher needs. It takes a bundle, installs what the bundle lists, and starts the game. It builds nothing: no config, no plugins.

sh
npm install @opys/runtime

Needs Node.js 20 or newer.

Example ​

js
import { launch, RuntimeError } from '@opys/runtime';

try {
  const child = await launch(
    { url: 'https://example.com/packs/game.opys' },
    {
      vars: {
        root: '/home/player/.local/share/my-pack',
        username: 'Player',
        uuid: '00000000-0000-0000-0000-000000000001',
        token: '0',
      },
      install: {
        onProgress(p) {
          if (p.phase === 'download')
            console.log(`${p.fetched}/${p.total} files`);
        },
      },
    },
  );
  child.on('exit', (code) => console.log('the game exited with', code));
} catch (err) {
  if (err instanceof RuntimeError) console.error(`${err.code}: ${err.message}`);
  else throw err;
}

Functions ​

FunctionInstallsStarts the gameReturns
install(source, options?)yesnonothing
launch(source, options?)yesyesChildProcess
prepare(source, options?)yesnoLaunchSpec
buildLaunch(source, options?)nonoLaunchSpec
spawnLaunch(spec)noyesChildProcess
readHead(source)nonoHead
currentPlatform()OsOptions

launch is prepare, then spawnLaunch. Use the two yourself to change what is started, or to spawn it your own way.

readHead is for the settings screen. It returns what the bundle says about itself, { format, options }, and options is what a player may set. It reads the head only: a bundle behind a URL is not downloaded, just its first bytes.

Where the pack comes from ​

The first argument of every function.

SourceWhat it is
{ url }A bundle on a server, read in pieces.
{ bundle }The path of a bundle on disk.
{ manifest }A manifest in memory. It cannot carry files, only download them.

A { url } is not downloaded whole. Its directory and manifest are read, a few hundred kilobytes, and then only the carried files the installation lacks, so a launch with nothing new costs no more than that. Every request after the first is pinned to the same version of the file with If-Range: a bundle replaced mid-install fails with a NetworkError rather than installing half of each. A server that serves no ranges, or names no version of its file, gets the bundle downloaded whole.

Options ​

OptionWhereWhat it does
varseverywhereValues for the bundle's variables.
featureseverywhereSwitches such as java_console.
platformeverywhereInstall for another OS or CPU. Default: this one.
cwdlaunch, prepareOverrides the working directory.
installlaunch, prepareInstall options, or false to skip installing.
onProgressinstallThe progress callback.
concurrencyinstallParallel downloads. Default 8.
verifyIntegrityinstallfalse skips hash checks. Leave it on.

Everything that belongs to the player's machine, such as root, is passed as vars. A bundle holds none of it.

Always pass root. Nothing is written or deleted outside it, and your value wins over the bundle's. A bundle that names a path anywhere else is refused before anything is downloaded, with the code manifest.

What comes back ​

LaunchSpec ​

What to start, with every variable filled in.

FieldTypeWhat it is
commandstringThe program.
argsstring[]Its arguments, for this machine.
workdirstringThe directory to start it in.
envsRecord<string, string>Environment variables to add.

Progress ​

onProgress is called with one of these. Tell them apart by phase.

phaseAlso hasWhen
resolveThe bundle is being read.
downloadfetched, total, skipped, bytes, totalBytesAfter each file. The totals.
download:startpath, totalBytesOne file begins.
download:bytespath, bytesOne file received more.
download:donepathOne file finished.
verifyHashes are being checked.
extractcountArchives are being unpacked.
cleanupremoved, directoriesStale files were deleted.

totalBytes is the sum of the sizes the bundle declares. It is 0 for a file listed without a size.

Errors ​

Every failure is a RuntimeError with a code. Decide from the code, never from the message.

codeClassAlso hasWhat happened
networkNetworkErrorurl, status, bodyA download failed.
integrityIntegrityErrorpathsA file did not match its hash.
extractionExtractionErrorartifactPath, causeAn archive could not be unpacked.
manifestRuntimeErrorThe bundle is wrong or unreadable.
ioRuntimeErrorA file could not be written.
cancelledRuntimeErrorThe install was stopped.
otherRuntimeErrorAnything else.

Every function ​

Each function, in each way it is used.

js
const source = { bundle: './game.opys' };
const vars = { root: '/games/pack', username: 'Player', uuid: '0000…', token: '0' };

// install only
await install(source, { vars });
await install(source, { vars, features: ['java_console'], concurrency: 16 });
await install(source, { vars, platform: { name: 'windows', version: '10.0', arch: 'x86_64' } });
await install({ url: 'https://example.com/game.opys' }, { vars, onProgress: (p) => console.log(p.phase) });

// install and start
const child = await launch(source, { vars });
await launch(source, { vars, install: false });          // already installed
await launch(source, { vars, cwd: '/games/pack/saves' }); // another working directory
await launch(source, { vars, install: { concurrency: 4 } });

// install, then start it yourself
const spec = await prepare(source, { vars });
// { command: '/games/pack/runtimes/jdk-21/bin/java', args: [...], workdir: '/games/pack/', envs: { JAVA_HOME: '…' } }
spawnLaunch({ ...spec, args: ['-Xmx8G', ...spec.args] });

// what would be started, with nothing installed
await buildLaunch(source, { vars });

// what a player may set, before anything is installed
const head = await readHead({ url: 'https://example.com/game.opys' });
// { format: 1, options: [{ slider: 'xmx', title: 'RAM', min: 2048, max: 16384, step: 512, default: 4096 }] }
await readHead({ manifest }); // undefined: a manifest in memory is in no bundle

// a manifest in memory: fine as long as it carries no files
await install({ manifest: { vars: {}, artifacts: [] } });

currentPlatform(); // { name: 'linux', version: '', arch: 'x86_64' }

Documentation ​

Part of opys.