Modular Computers the manual GitHub

Persistence

You can stop the server, or walk away so that the chunk holding your computer unloads, and when you come back the computer goes on exactly where it was: in the middle of a loop, in the middle of a wait, with its variables, its open files and its screen. It does not reboot. This page explains how, and what it costs.

The Lua engine

The computers run Lua 5.5 on LungoLua and StyxLua, a Java port of a Lua 5.5 fork with three things stock Lua doesn’t have:

It is written entirely in Java. There is no native binary to install, so the same jar runs on any machine that runs Minecraft, on both Fabric and NeoForge.

Three layers

A computer runs three layers, as a real one runs firmware under an operating system.

  1. The host is the Java side. A runner on the computer’s own thread (a low-priority thread of its own) runs the Lua virtual machine. Anything that touches the world, such as a device or a block, is done for it on the server thread, and the computer’s thread waits for the answer.
  2. The firmware (machine.lua) is the CPU’s layer. It is unbudgeted, and user code cannot reach it. It builds the sandbox, loads the EEPROM’s program, budgets and freezes it, and answers its syscalls and its waits for interrupts.
  3. User code is everything budgeted: the EEPROM’s program, and with mcos the kernel and every program. The program that runs first is the root of all user threads.

User code never sees the firmware’s variables, the real globals or debug.

What a budget means

There are three limits.

Freezing, not yielding

A syscall does not yield. Every board call, wait or halt records its request and cuts the program’s budget to one step; the budget callback then freezes the whole chain of the program’s coroutines, wherever it is: inside coroutines, inside a table.sort comparison, inside a gsub callback. When the host has answered, the program thaws and the call returns the answer.

So no coroutine in your program ever sees a yield, and pcall cannot catch a freeze. A program needs no special care to be saved, or to be stopped by its budget.

mcos uses this itself. Its kernel runs every program as a coroutine, stops one with a trap (the syscall instruction) and resumes the next: see The sys library.

Saving a running computer

When the world saves, or a chunk unloads, the server asks each running computer for a snapshot. The computer’s thread honours it at its next safe point, with no call in flight. A save never splits a program from its devices: a call that was queued but not yet made is a safe point itself, so the program and the devices it talks to are saved in agreement.

Then StyxLua writes the entire state of the Lua machine out as bytes, and they are stored with the block in the chunk.

There are no timeouts and no kills. If a program is deep in a long step, the save waits until it reaches the next slice. The server logs a computer that took long to save, naming it.

What survives

After a restore, the computer is simply running.

What does not

Why

A world where every chunk load reboots every computer would be a world where long-running programs are impossible: a sorter, a farm controller, a server. So computers keep their state, as a real machine in suspend-to-disk does, and the engine had to be able to write a program out whole. That is what LungoLua and StyxLua are for.