Skip to main content

ScrapComputers Emulation Tool

Project description

EmuSC

EmuSC is a desktop emulator for ScrapComputers, a Lua computer mod for Scrap Mechanic. It lets you run and test ScrapComputers scripts on your machine without launching the game.

Scripts run against simulated hardware: displays render in a window, a terminal handles text I/O, keyboard input is captured, motor physics are ticked, and so on. The Lua environment exposes the same API the mod provides in-game - those are documented at scrapcomputers.dev/docs. This README covers the emulator itself: how to run it, how to declare hardware, and where it deviates from the game.

Installation

pip install EmuSC

Requires Python 3.10 or newer.

Usage

python -m EmuSC <script.lua> [options]

Options:

Flag Description
-H SPEC, --hardware SPEC Attach a hardware component (repeatable)
-c FILE, --config FILE Load hardware configuration from a JSON file
-d DIR, --data-dir DIR Directory for persistent hardware data (default: .)
-t HZ, --tick-rate HZ Simulation ticks per second (default: 40)
--dev Echo Lua print() output to stdout in addition to the terminal window
-D, --debug Open the debugger window (see below)
-B, --break-on-start Pause before onLoad so breakpoints can be set first (implies --debug)
-r DIR, --require-path DIR Directory searched by require() (default: directory of the script)

Hardware

Components are declared with -H flags or via a JSON config file. Both can be combined; the config is loaded first, then -H flags are appended.

Inline syntax

-H <type>[:<param>[:<param>...]]

Available components

Type Spec Notes
display display[:WxH[:scale]] Renders to a window. Default size is 160x120 at scale 2. scale multiplies the window resolution.
display_file display_file[:WxH] Writes {width, height, pixels} (packed RGB ints) to a pixels.json in the data directory instead of opening a window. Useful for headless testing, or custom display runtime.
terminal terminal Text I/O window. send() writes to it; typed input is queued for getInput().
keyboard keyboard Keystroke capture window.
motor motor Simulates a bearing or piston with adjustable speed, angle, and length.
gps gps Provides position and velocity data, editable in the UI.
radar radar Simulates radar returns.
laser laser Distance measurement and hit detection.
speaker speaker Audio stub (not yet implemented).
light light Light control.
hdd hdd[:label] Persistent storage backed by a JSON file. The label distinguishes multiple drives.
network network[:channel] Channel-based packet messaging between network ports.
antenna antenna Long-range relay between network ports. Named via the JSON config.
hologram hologram 3D holographic display.
inputreg inputreg[:name[:value]] Named numeric input register, editable in the Register Manager window.
outputreg outputreg[:name] Named numeric output register, shown as a bar.

JSON config format

{
  "hardware": [
    { "type": "display", "width": 320, "height": 240, "scale": 2 },
    { "type": "terminal" },
    { "type": "keyboard" },
    { "type": "hdd", "label": "main" },
    { "type": "antenna", "name": "tower" },
    { "type": "network", "channel": "eth0", "antenna": "tower" },
    { "type": "inputreg", "name": "throttle", "value": 0.5 }
  ]
}
emusc script.lua -c hardware.json

A few components take extra keys that have no inline equivalent: gps accepts starting x/y/z and vx/vy/vz, motor takes max_length, terminal takes max_lines, laser takes distance, max_range and has_hit, and network takes antenna to link the port to a named antenna.

Debugger

Run with -D / --debug to open the debugger window alongside the simulation. It has four tabs:

  • Console - mirrors all Lua print() output and runtime errors (regardless of whether --dev is set), and takes commands. Anything that isn't a command gets evaluated as a Lua expression. Up/Down arrows cycle through history.
  • Stack - the Lua call stack, refreshed on breakpoint hits and errors (or manually).
  • Variables - lists every global variable defined by your script (updated each tick), with its type and current value. Tables show a key count; functions show the source location. While paused at a breakpoint, the locals captured at that line appear here too.
  • Breakpoints - enter a line number and click Add (or use bp in the console). When execution reaches that line the simulation pauses mid-tick with a snapshot of the locals. Click Resume to continue or Step to advance one tick and pause again.

The console commands, in short:

g / p / step      resume, pause, run one tick
bp N  bc N  bl    set / clear / list breakpoints
k                 print the Lua call stack
? <expr>          evaluate a Lua expression
dv [name]         dump all variables, or inspect one
tick              print the tick counter

Expression eval is disabled while paused at a breakpoint (the Lua VM is suspended inside the debug hook at that point), use dv or the Variables tab instead.

The Pause and Resume buttons in the toolbar also work outside of breakpoints - Pause halts the tick loop at the next tick boundary, Resume restarts it.

Starting with -B / --break-on-start pauses before onLoad runs, so you can place breakpoints before any of your code executes.

Modules and require()

require() in EmuSC takes a direct file reference, extension included:

-- if your script is at ~/projects/myapp/main.lua,
-- this loads ~/projects/myapp/utils.lua
local utils = require("utils.lua")

The require() function is identical to ScrapComputer's version of it. However, it resolves against a certain path, root path by default, instead of computer filesystem which, well, doesn't exist.

To resolve against a different root instead, pass --require-path:

emusc main.lua -H terminal --require-path ./lib

Writing scripts

Scripts use the same structure as in-game ScrapComputers programs:

Scripts are checked against Lua 5.1 syntax before they run: goto and labels, floor division, the 5.3 bitwise operators, <const>/<close> attributes and \x/\z/\u{} string escapes are all rejected with a line number, since the mod's VM would choke on them in-game. A bit library is available for bitwise work.

The emulator sets a global _EmuSC = true that never exists in-game. Use it to add emulator-only setup or to skip code that requires real hardware:

function onLoad()
    if _EmuSC then
        print("running in emulator")
    end
end

For everything else, component getters, drawing calls, packet formats and so on, the API behaves as the ScrapComputers docs describe.

What it doesn't do

There is no game world behind the hardware, so anything that would measure the world is fed by hand instead:

  • GPS, laser and radar values are whatever you enter in their windows. The GPS integrates velocity into position each tick; its rotation and acceleration fields stay zeroed.
  • Holograms are tracked (create, move, delete all work) but nothing renders them.
  • The speaker is silent - playNote() and similar just flash what would have played in its window.
  • Cameras, seat controllers, gravity controllers, power sources and radar warning receivers aren't simulated. Their getters return empty tables, so loops over them are safe.
  • sc.power reports fixed values; sc.midi, sc.nbs and sc.qrcode return nil; the audio, language, configuration and example managers are stubs.
  • sc.lz4 round-trips its own output but is not wire-compatible with real LZ4, and sm.noise is a cheap approximation rather than true Perlin noise.

There is also no timeout or watchdog: if onUpdate contains a loop that never exits, the next tick never fires and the emulator freezes in place, matching in-game behavior where the computer simply stops responding. A stuck script stays stuck until you close the window.

Example

local display, terminal, keyboard

function onLoad()
    display  = sc.getDisplays()[1]
    terminal = sc.getTerminals()[1]
    keyboard = sc.getKeyboards()[1]
    terminal.send("Ready.\n")
end

function onUpdate(dt)
    local key = keyboard.getLatestKeystroke()
    if key and key ~= "" then
        terminal.send("Key: " .. key .. "\n")
    end

    display.clear(sm.color.new("001122"))
    display.drawText(10, 10, "Hello", sm.color.new("ffffff"), nil)
    display.update()
end
emusc myscript.lua -H display:320x240:2 -H terminal -H keyboard --dev

Persistent data

Each component gets a folder under <data-dir>/EmuSC/Hardware/, named after its type and index. HDDs keep a data.json in theirs; display_file components write their pixels.json to the same tree. The data directory defaults to the current working directory; use --data-dir to change it:

emusc script.lua -H hdd:main -d ./saves

This writes to ./saves/EmuSC/Hardware/hdd_0/data.json.

License

License text copyright © 2024 MariaDB plc, All Rights Reserved. “Business Source License” is a trademark of MariaDB plc.

Terms

The Licensor hereby grants you the right to copy, modify, create derivative works, redistribute, and make non-production use of the Licensed Work. The Licensor may make an Additional Use Grant, above, permitting limited production use.

Effective on the Change Date, or the fourth anniversary of the first publicly available distribution of a specific version of the Licensed Work under this License, whichever comes first, the Licensor hereby grants you rights under the terms of the Change License, and the rights granted in the paragraph above terminate.

If your use of the Licensed Work does not comply with the requirements currently in effect as described in this License, you must purchase a commercial license from the Licensor, its affiliated entities, or authorized resellers, or you must refrain from using the Licensed Work.

All copies of the original and modified Licensed Work, and derivative works of the Licensed Work, are subject to this License. This License applies separately for each version of the Licensed Work and the Change Date may vary for each version of the Licensed Work released by Licensor.

You must conspicuously display this License on each original or modified copy of the Licensed Work. If you receive the Licensed Work in original or modified form from a third party, the terms and conditions set forth in this License apply to your use of that work.

Any use of the Licensed Work in violation of this License will automatically terminate your rights under this License for the current and all other versions of the Licensed Work.

This License does not grant you any right in any trademark or logo of Licensor or its affiliates (provided that you may use a trademark or logo of Licensor as expressly required by this License).TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE. MariaDB hereby grants you permission to use this License’s text to license your works, and to refer to it using the trademark “Business Source License”, as long as you comply with the Covenants of Licensor below.

Covenants of Licensor In consideration of the right to use this License’s text and the “Business Source License” name and trademark, Licensor covenants to MariaDB, and to all other recipients of the licensed work to be provided by Licensor:

To specify as the Change License the GPL Version 2.0 or any later version, or a license that is compatible with GPL Version 2.0 or a later version, where “compatible” means that software provided under the Change License can be included in a program with software provided under GPL Version 2.0 or a later version. Licensor may specify additional Change Licenses without limitation. To either: (a) specify an additional grant of rights to use that does not impose any additional restriction on the right granted in this License, as the Additional Use Grant; or (b) insert the text “None” to specify a Change Date. Not to modify this License in any other way.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

emusc-1.0.2.tar.gz (44.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

emusc-1.0.2-py3-none-any.whl (56.4 kB view details)

Uploaded Python 3

File details

Details for the file emusc-1.0.2.tar.gz.

File metadata

  • Download URL: emusc-1.0.2.tar.gz
  • Upload date:
  • Size: 44.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for emusc-1.0.2.tar.gz
Algorithm Hash digest
SHA256 e72454b15a5422c4ea23215c4d624f4cc9e994971ea43eb5ea4c7cfb0542e034
MD5 ea958935d0964a75fc5b94f6df4cbdca
BLAKE2b-256 4ebf0fd090a50d4fea8fa525d59910cd1837770bf5c81024bab5eded23ccb29d

See more details on using hashes here.

File details

Details for the file emusc-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: emusc-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 56.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for emusc-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 02091411506bfd9f8172bcb26bc7b322b72b4801d5be773a115d2b669c5b3623
MD5 a32c6b5abed920b8bac48b9e37502f4b
BLAKE2b-256 1e05c3aa2b92fd7f629b738d12d0c505003faa2490c9dd76ae6c0380709aa036

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page