Do I need this page?

Prerequisite: Core Concepts

๐ŸŽฏ Quick Navigation โ€” Most addons only need 3โ€“4 methods
Sensors: readDigitalInput(), readAnalogInput()
Outputs: applyDigitalOutputs(), applyAnalogOutputs()
World data: isRaining(), getLightLevel(), worldTimeTicks()
Display: publishDisplayMessage()
Use Ctrl+F to search โ€” treat this page as a lookup table, not a read-through.

On this page

What is the Execution Context?

ISpcExecutionContext is created fresh each server tick and passed to every compiled node's execute(context, state) method. It provides read-only world access (sensors, inputs) and write-only world actions (outputs, effects, display messages).

All methods have safe defaults โ€” if the physical multiblock doesn't support a feature (e.g., no FE modules), the method silently returns a neutral value or does nothing. Your node code never needs null-checks.

When is it available?

PhaseContext StateWhat your node should do
INPUT_READFresh, inputs sampledRead physical/virtual inputs, write to signal bus
LOGIC_EVALUATIONSame contextRead upstream signals from bus, compute logic, write results to bus
STATE_UPDATESame contextUpdate timers, counters, latches; read from bus and context
OUTPUT_APPLYSame contextRead bus, push values to physical outputs via context
โ„น๏ธ Context vs. State
ISpcExecutionContext = world interaction (Minecraft blocks, sensors, effects).
ISpcRuntimeState = signal bus + persistent data (signals between nodes, counters, latches).
Both are passed to execute(). Use context for the world, state for the program.

Digital I/O (2 methods)

MethodReturnsParametersDefaultDescription
readDigitalInput(int channel) boolean channel โ€” 1-based input channel number โ€” Read the on/off state of a physical digital input block at the given channel
applyDigitalOutputs(Map<Integer,Boolean>) void outputStates โ€” map of channel โ†’ boolean โ€” Write on/off values to physical digital output blocks
โš ๏ธ No defaults
These two methods are not default methods โ€” they are always implemented by the SPC runtime. They are the core I/O methods of any LOGO machine.

Analog I/O (2 methods)

MethodReturnsParametersDefaultDescription
readAnalogInput(int channel) int channel โ€” 1-based input channel 0 Read the integer signal level from an analog input block (e.g., comparator reading)
applyAnalogOutputs(Map<Integer,Integer>) void outputStates โ€” map of channel โ†’ integer value no-op Write integer values to analog output blocks

FE (Forge Energy) I/O (2 methods)

MethodReturnsParametersDefaultDescription
readFeInput(int channel) int channel โ€” 1-based FE input channel 0 Read stored FE amount from FE I/O block at this channel
applyFeOutputs(Map<Integer,Integer>) void feOutputs โ€” map of channel โ†’ FE amount to transfer no-op Transfer FE to adjacent machines via FE output blocks

Virtual Signals (6 methods)

Virtual signals are named global variables shared across all nodes in a program. They're useful for cross-node communication without wires.

MethodReturnsParametersDefaultDescription
readVirtualDigital(String sourceKey) boolean sourceKey โ€” named signal key false Read a boolean virtual signal
readVirtualInteger(String sourceKey) int sourceKey โ€” named signal key 0 Read an integer virtual signal
readVirtualDecimal(String sourceKey) double sourceKey โ€” named signal key 0.0 Read a decimal virtual signal
writeVirtualDigital(String sourceKey, boolean value) void sourceKey, value no-op Write a boolean virtual signal
writeVirtualInteger(String sourceKey, int value) void sourceKey, value no-op Write an integer virtual signal
writeVirtualDecimal(String sourceKey, double value) void sourceKey, value no-op Write a decimal virtual signal
// Write a virtual signal from one node:
context.writeVirtualInteger("mymod:temperature", 350);

// Read it from another node (same tick, later phase):
int temp = context.readVirtualInteger("mymod:temperature");

Time (2 methods)

MethodReturnsParametersDefaultDescription
worldTimeTicks() long โ€” 0L Current world time in ticks (absolute, not day-of-cycle)
isDaytime() boolean โ€” Computed true when worldTimeTicks() % 24000 < 12000

Display (2 methods)

MethodReturnsParametersDefaultDescription
publishDisplayMessage(UUID nodeId, String message, boolean active) void nodeId โ€” the publishing node; message โ€” text; active โ€” highlight state no-op Send a text message to the LOGO display unit attached to this machine
publishExternalDisplayMessage(String targetIp, UUID nodeId, String message, boolean active) void targetIp โ€” network display IP; nodeId; message; active no-op Send text to an external display unit on the network

World Sensors (9 methods)

MethodReturnsParametersDefaultDescription
isRaining() boolean โ€” false Whether it's currently raining at the machine's position
isThundering() boolean โ€” false Whether there's currently a thunderstorm
getLightLevel(int offsetX, int offsetY, int offsetZ) int Block offset from machine anchor 0 Light level (0โ€“15) at the offset position
getPlayerDistance() int โ€” Integer.MAX_VALUE Distance in blocks to the nearest player
countEntitiesInRadius(int radius, String entityType) int radius โ€” block radius; entityType โ€” registry ID (e.g., "minecraft:zombie") 0 Count entities of a specific type within the radius
isBlockAt(int offsetX, int offsetY, int offsetZ, String blockId) boolean Block offset + block registry ID false Check if a specific block type exists at the offset position
getContainerFillLevel(int offsetX, int offsetY, int offsetZ) int Block offset 0 Container fill level (0โ€“15, like comparator) at offset
getBiomeId() String โ€” "" The biome registry ID at the machine's position (e.g., "minecraft:plains")
getMoonPhase() int โ€” 0 Current moon phase (0โ€“7). 0 = full moon.
// Example: Read nearby entities
int zombieCount = context.countEntitiesInRadius(32, "minecraft:zombie");

// Example: Check what block is nearby
boolean hasFurnace = context.isBlockAt(0, -1, 0, "minecraft:furnace");

Effects (4 methods)

MethodReturnsParametersDefaultDescription
playNote(int instrument, int pitch) void instrument โ€” note block instrument ID; pitch โ€” note (0โ€“24) no-op Play a note block sound at the machine position
emitParticles(String particleType, int count) void particleType โ€” particle registry ID; count โ€” number of particles no-op Spawn particles at the machine position
launchSignalFlare(int color) void color โ€” dye color ordinal no-op Launch a visual flare above the machine (alarm/notification)
sendChatMessage(UUID nodeId, String sender, String message) void nodeId โ€” source node; sender โ€” display name; message โ€” text no-op Broadcast a chat message to nearby players

Items (4 methods)

MethodReturnsParametersDefaultDescription
readItemInputId(int channel, int slot) String channel โ€” I/O channel; slot โ€” inventory slot index "" Read the item registry ID from an input inventory slot
readItemInputCount(int channel, int slot) int channel; slot 0 Read the stack size from an input inventory slot
readInventoryFingerprint(int channel) long channel โ€” I/O channel 0L Hash of entire inventory contents โ€” changes when any slot changes
applyItemOutputs(Map<Integer,String> itemIds, Map<Integer,Integer> itemCounts) void itemIds โ€” channel โ†’ item ID; itemCounts โ€” channel โ†’ count no-op Request item transfers to output inventory slots

Data Logging (1 method)

MethodReturnsParametersDefaultDescription
recordDataLog(UUID nodeId, Map<String,SpcSignalValue> values) void nodeId โ€” source node; values โ€” named signal snapshots no-op Record signal values for data logging/trending displays

Custom Module I/O (2 methods)

These methods bridge addon-registered multiblock modules (see Multiblock Modules) to the execution context. When a node needs to read from a custom input module or write to a custom output module, it uses these methods.

MethodReturnsParametersDefaultDescription
readCustomInput(String moduleTypeId, int channel) SpcSignalValue moduleTypeId โ€” registered type (e.g., "mymod:rotational_input"); channel โ€” 1-based INTEGER_ZERO Read a value from a custom input module's channel
applyCustomOutputs(String moduleTypeId, Map<Integer,SpcSignalValue> values) void moduleTypeId; values โ€” channel โ†’ signal value no-op Write values to custom output module channels
// Read from a custom rotational input module:
SpcSignalValue rpm = context.readCustomInput("mymod:rotational_input", 1);
int speed = rpm.asInteger();

// Write to a custom output module:
context.applyCustomOutputs("mymod:rotational_output",
    Map.of(1, SpcSignalValue.integer(targetSpeed)));

Extension Mechanism (1 method)

For complex world interactions that go beyond the built-in methods, addons register custom ISpcExecutionContextExtension implementations. See the Context Extensions guide for full details.

MethodReturnsParametersDefaultDescription
getExtension(Class<T> extensionType) Optional<T> extensionType โ€” the extension interface class Optional.empty() Retrieve a registered addon extension for custom world interactions
// In your compiled node:
Optional<ICreateRotationalContext> ext = context.getExtension(ICreateRotationalContext.class);
ext.ifPresent(rotCtx -> {
    int speed = rotCtx.getRotationalSpeed(blockPos);
    state.setSignal(outputAddr, SpcSignalValue.integer(speed));
});

Method Count Summary

CategoryMethodsHas Default?Typical Node Phase
Digital I/O2NoINPUT_READ / OUTPUT_APPLY
Analog I/O2YesINPUT_READ / OUTPUT_APPLY
FE I/O2YesINPUT_READ / OUTPUT_APPLY
Virtual Signals6YesAny
Time2YesINPUT_READ
Display2YesOUTPUT_APPLY
World Sensors9YesINPUT_READ
Effects4YesOUTPUT_APPLY
Items4YesINPUT_READ / OUTPUT_APPLY
Data Logging1YesOUTPUT_APPLY
Custom Module I/O2YesINPUT_READ / OUTPUT_APPLY
Extensions1YesAny
Total37