Do I need this page?

Prerequisite: Core Concepts

On this page

What are Runtime Events?

Runtime events are lifecycle hooks that fire at critical moments in a LOGO machine's operation. They let addon mods:

ComponentPackagePurpose
ISpcRuntimeEventListenerapi.eventInterface with 4 default events to override
SpcRuntimeEventRegistryapi.eventWhere you register your listener

Machine Lifecycle

This diagram shows when each event fires in the LOGO machine lifecycle:

Blocks Placed onMultiblockAssemble (level, machinePos) Structure Valid onProgramStart (level, machinePos) Running (20 tps) onProgramStop (level, machinePos) onMultiblock Disassemble (block broken or structure invalid) user starts

ISpcRuntimeEventListener โ€” All 4 Events

All methods are default โ€” override only the ones you need. Both parameters are always provided by the SPC runtime.

Event MethodParametersWhen It Fires
onProgramStart(ServerLevel level, BlockPos machinePos) level โ€” the server level
machinePos โ€” processing unit anchor position
A LOGO program begins execution on a machine
onProgramStop(ServerLevel level, BlockPos machinePos) Same as above A running program is stopped (manual stop, error, or chunk unload)
onMultiblockAssemble(ServerLevel level, BlockPos machinePos) Same as above Multiblock structure is successfully validated
onMultiblockDisassemble(ServerLevel level, BlockPos machinePos) Same as above Multiblock breaks (block removed, structure invalidated)

Firing order guarantees

GuaranteeDetails
Assemble before StartonMultiblockAssemble always fires before onProgramStart
Stop before DisassembleIf a program is running, onProgramStop fires before onMultiblockDisassemble
All listeners calledAll registered listeners are iterated in registration order
Server-side onlyEvents only fire on the logical server (ServerLevel)

SpcRuntimeEventRegistry

MethodReturnsParametersDescription
register(ISpcRuntimeEventListener) void listener โ€” non-null Register a listener. Must call during mod init (before freeze).
all() List<ISpcRuntimeEventListener> โ€” Unmodifiable snapshot of all registered listeners
freeze() void โ€” Internal โ€” do not call

Registration uses CopyOnWriteArrayList internally โ€” thread-safe even during parallel mod loading.

Use Cases for Each Event

EventUse CaseExample
onProgramStart Initialize per-machine addon state Allocate a rotation history buffer for trending
onProgramStart Start external monitoring Begin logging to a file or network dashboard
onProgramStop Clean up per-machine state Release buffers, flush logs, reset counters
onProgramStop Safety shutdown Set all addon actuators to safe positions
onMultiblockAssemble Scan for addon modules Find your custom blocks in the structure
onMultiblockAssemble Show notifications Log that a machine with addon modules was built
onMultiblockDisassemble Invalidate cached references Drop references to block entities in the structure
onMultiblockDisassemble Emergency stop Cut power to connected machines immediately

Complete Example

public class MyAddonListener implements ISpcRuntimeEventListener {
    private final Map<BlockPos, AddonState> machines = new HashMap<>();

    @Override
    public void onMultiblockAssemble(ServerLevel level, BlockPos machinePos) {
        LOGGER.info("LOGO machine assembled at {}", machinePos);
    }

    @Override
    public void onProgramStart(ServerLevel level, BlockPos machinePos) {
        machines.put(machinePos, new AddonState());
        LOGGER.info("Program started at {}", machinePos);
    }

    @Override
    public void onProgramStop(ServerLevel level, BlockPos machinePos) {
        AddonState state = machines.remove(machinePos);
        if (state != null) state.cleanup();
        LOGGER.info("Program stopped at {}", machinePos);
    }

    @Override
    public void onMultiblockDisassemble(ServerLevel level, BlockPos machinePos) {
        machines.remove(machinePos);
        LOGGER.info("LOGO machine disassembled at {}", machinePos);
    }
}

// In your @Mod constructor:
SpcRuntimeEventRegistry.register(new MyAddonListener());

Tips & Pitfalls

DoDon't
Keep event handlers fast โ€” they run on the server thread Don't do heavy I/O or network calls synchronously
Use machinePos as a key for per-machine state Don't store world references โ€” they can leak memory
Clean up in onProgramStop and onMultiblockDisassemble Don't assume both fire โ€” blocks can break without program stop if never started
Register during mod init (constructor or FMLCommonSetupEvent) Don't register after freeze โ€” it will throw IllegalStateException