LuaComb++
Overview
LuaComb++ executes restricted Lua scripts inside a circuit combinator. A script can read the green and red input wires, combine their signals, retain controlled data between executions, and produce independent persistent outputs.
The project began as a fork of Moon Logic 2 by Chilla55 and has since been extensively reworked around a guided editing workflow, Factorio 2.0 signal qualities, deterministic execution, and multiplayer safety.
Main features
Circuit interface
- Read the green wire with
g, the red wire withr, or their combined total withget. - Address signals by name, type, and quality.
- Aggregate all qualities or select one exact numeric quality level.
- Write independently to the green or red output wire.
- Keep outputs active until they are changed, removed with count
0, or cleared explicitly.
State and scheduling
- Store private per-combinator data in
self. - Share data-only global variables between LuaComb++ combinators belonging to the same force.
- Choose an execution interval from the editor: every tick, every 6 ticks, or once per second.
- Preserve the last valid outputs if execution fails.
Guided editor
- Live input and output signal panels.
- Contextual suggestions for Lua, signals, properties, and LuaComb++ functions.
- Factorio signal picker for exact signal descriptors.
- Script formatting and translated Help.
- Recovery of the last successfully compiled script.
- Personal library for up to ten named scripts.
- Settings copy and blueprint support.
Runtime protection
- Restricted, deterministic Lua environment.
- Limits for instructions, source size, strings, tables, nesting, persistent data, and signals.
- Isolated failure handling for each combinator.
- Force-scoped alerts for connected players when a script is disabled.
Design lineage and focus
Moon Logic 2 and LuaComb++ share the same central idea: express circuit-network logic with Lua. Their interfaces emphasize different styles of use.
| Area | Moon Logic 2 approach | LuaComb++ approach |
|---|---|---|
| Editing workflow | Compact code window designed for short scripts and pasted logic | Guided editor with live panels, completion, formatting, Help, and recovery tools |
| Circuit access | red, green, and out tables |
Quality-aware g, r, and get records with explicit output functions |
| Signal identity | Direct name-based wire access | Explicit type + name + quality identity with convenient aggregate access |
| Persistent state | Per-combinator var data |
Per-combinator self data plus force-wide shared globals |
| Scheduling | Script-controlled delay and irq values |
Per-combinator execution profile selected from the editor |
| Output style | Assignment through the persistent out table |
Persistent set, g.set, r.set, and clear operations |
| Runtime goal | Lightweight programmable circuit transformations | Bounded execution, deterministic shared state, and controlled multiplayer behavior |
| Mods data | Great feuture wich provide data from prototypes and other mods stuff | Not implemented |
LuaComb++ keeps the programmable-combinator concept while offering a workflow aimed at players who want to build and debug scripts directly inside the game.
Quick start
- Place a LuaComb++ combinator.
- Connect its input and output circuit wires.
- Open the editor and choose an execution interval.
- Write the script.
- Press Compile, validate and save.
Editor changes remain drafts until validation succeeds.
local plates = get["iron-plate"].count
if plates < 100 then
g.set("signal-A", 1)
else
r.set("signal-A", 1)
end
Script reference
Reading signals
Every signal record exposes:
| Property | Meaning |
|---|---|
.count |
Signed signal quantity |
.quality |
Numeric quality level |
.quality_level |
Alias of .quality |
.type |
Signal prototype type |
g["iron-plate"].count -- all qualities on green
r["iron-plate"][2].count -- exact quality level 2 on red
get["iron-plate"].count -- all qualities on both wires
get["iron-plate"][0].count -- base quality on both wires
Unqualified access aggregates every quality. Its .quality is the highest quality whose count is not zero, including negative signals. A valid signal that is absent returns count and quality level 0.
Use a complete descriptor when the signal type must be explicit:
get[{name = "iron-plate", quality = 2, type = "item"}].count
Writing signals
set(signal, count [, quality [, type]])
g.set(signal, count [, quality [, type]])
r.set(signal, count [, quality [, type]])
setwrites to both output wires.g.setwrites only to green.r.setwrites only to red.- Count
0removes the exact signal identity. clear()removes every stored output.
Outputs persist across execution cycles, recompilation, saving, and loading until a script changes them explicitly.
Persistent data
Use self for data owned by one combinator:
self.counter = (self.counter or 0) + 1
Use a normal global variable for data shared by every LuaComb++ combinator in the same force:
production_target = (production_target or 0) + 100
Only data can persist. Functions should normally be declared local. A successful recompilation resets self; destroying the combinator removes its local state.
Complete example: seven-segment counter
This example cycles a display through digits 0 to 9.
Setup
- Import the blueprint provided below or build a seven-segment display.
- Connect virtual signals
signal-Athroughsignal-Gto their corresponding segments. - Connect the LuaComb++ green output wire to the display.
- Select 1 execution per second in the editor.
- Compile and save the following script.
Script

self.segments = {
'ABCEFG', 'CF', 'ACDEG', 'ACDFG', 'BCDF',
'ABDFG', 'ABDEFG', 'ACF', 'ABCDEFG', 'ABCDFG'
}
self.tickCount = self.tickCount or 1
local pattern = self.segments[self.tickCount]
clear()
for i = 1, string.len(pattern) do
local letter = tostring(string.sub(pattern, i, i))
g.set(concat({'signal-', letter}), 1)
end
self.tickCount = self.tickCount + 1
if self.tickCount >= 11 then
self.tickCount = 1
end
Execution sequence
self.segmentscontains exactly ten patterns. Table indices1through10represent digits0through9.self.tickCountstarts at1and survives between successful executions.- The current counter value selects the pattern for the digit.
clear()removes the preceding digit because output signals are persistent.- The loop extracts one letter at a time with
string.sub. concatbuilds signal names such assignal-A.g.setactivates every required segment on the green output.- After index
10displays digit9, the counter returns to1.
At one execution per second, each digit remains visible for one second. Recompiling the script resets self, so the sequence restarts from digit 0.
Blueprint
Copy the complete string and import it through Factorio's Import string command:
0eNrVWO1u2jAUfZXIf4DNIBJI+JBWaYW2D1EQMsGANceJbIetQnn3XdsESos2wh8ahMjJ9bkfx7l2QvZoyXOaSSY0
Gu8Ri1Oh0Ph1jxTbCMKNTZCEojFSCeG8zUmSoQIjJlb0Dxr7Bb5A5TmJ02SZZW1zYILoVL5zCoo5RlRophl1yezJ20LkyZJKiIr/
GQmjLFXgnAqT01TRCTF6s9WgFZM0dmNDjECOlilfLOmW7Bj4ggORTG8Tqlm8gOGVDaTMgKLm3BiVJmY
+uhilGZXEhUPfUAEJNNlY9rE047Bmm7zkQaB4SxNidcTpyk4e5euOopsElCrvh7dv/HycPD2
/NPAJTJ7N2WT69OKO1vgIR0uaHsjTg5djw/AximEUM2FTgbhfkzQXGnJ9MHip9PyZ4GlMuJcRrakUJaus8PXcZ
z4TMadENlszsQZ3Bnwfe0pD22w6nIrmIU7LW6Uz4cHHhefUmIGtU0duHnxUvix9sMfg22o5Pxiiugkz
GhPd3Ddca7Ub2EUqWtgHIhWr/+v8blTOBFt/HHiA4n1Pb6nwIOenML6Nj0y3Qsadaeyoa/qVLDldobGWOS0K0/cfujbAF5bKp15tdw/
N2uuExeUGjZmMc6YXZynx0XxsWsNdM6n04rQC9VtmKtgxqXOwnEpyE/mIXM5Tg0MPZ0TadTVGD+iist5VyspVGNxD2M
+bhPWvEtatn7DwKmG9e7bi5CZh0VXCgvpdsUG13aNfn91jWGn3CO8hbHqTsFGl3aNGwvxupVVWJ2V+pY2xX5+N0a/4+
DG4h7Sn26T1qkmLaiStX6kf73LRnm9TVu0RJKqRsqjSDW14D2UvtykbVLqjfVFlGOWKQg6eSlX+XbugdVjpHv
dlr+Ico99M2vcorz7u4x4O5vg1wAHgEg0s6h1tBkUWhWcI7o4GAh2PLBqeIXgsMHBkoEtjLKWXw6HDwTuOxa4
E2O5OHIuHDkfvOFFpB2lM0wSUnt5RYbSjUtnpDKNg1B+NwmE/GsBPUfwFSkhVkw==
Safety and multiplayer
LuaComb++ scripts run inside a bounded sandbox. Factorio objects, metatables, coroutines, dynamic code loading, and unrestricted libraries are unavailable.
If compilation, input handling, or runtime execution fails:
- Only the affected combinator is disabled.
- Its last valid outputs remain active.
- Connected players belonging to the same force receive an alert.
- The last valid script and state remain available for recovery.
Scripts imported from untrusted blueprints should always be reviewed before being enabled.
Credits
LuaComb++ is derived from:
- Moon Logic 2 by Chilla55 and contributors
- Moon Logic by mk-fg
- Sandboxed LuaCombinator by IWTDU
- LuaCombinator 2 by OwnlyMe
Original and upstream contributors include OwnlyMe, IWTDU, mk-fg, smartguy1196, and Chilla55.
I work on it when i have time...sorry :(