File Details
SPC Core v6 for SPC 1.1.0 (1.21.1)
- R
- Apr 25, 2026
- 77.98 KB
- 11
- 1.21.1
- NeoForge
File Name
storedprogramcontrols-v6-api.jar
Supported Versions
- 1.21.1
Curse Maven Snippet
Stored Program Controls — API Changelog
Versioning note: starting with this release, API jars use a flat "vN" naming scheme — the integer matches SpcApi.API_VERSION. Older semver-style names (1.x.y) ended at 1.5.0; v6 covers everything that was previously 1.2.0 through 1.5.0, plus the renaming itself.
API v6 (April 21–24, 2026)
Consolidated entry covering everything previously released as 1.2.0, 1.3.0, 1.4.0, and 1.5.0. SpcApi.API_VERSION is 6. Jar: storedprogramcontrols-v6-api.jar.
Help Page API (com.hypernova.spc.api.help)
Addons can publish rich help pages for their function blocks. When a player opens the Block Help dialog on a node whose typeId is not in the built-in catalog, the registry is consulted and the registered page is rendered with the same layout as built-in entries.
- SpcHelpRegistry: thread-safe, freezable; map node typeId → SpcHelpPage.
- SpcHelpPage: immutable record built via a fluent Builder. Builder ops: builder(title, shortDescription), connection(label, signalType, desc), parameter(label, desc), row(SpcHelpRow), paragraph(text), logicTable(...), diagram(SpcHelpDiagram).
- SpcHelpSignalType enum: DIGITAL, ANALOG, INTEGER, DECIMAL, STRING, ITEM, ITEM_ID, PARAMETER, OTHER. displayName() drives the "Signal Type" column in the connection table.
- SpcHelpRow, SpcHelpLogicTable (column widths validated at construction), SpcHelpDiagram (texture, widthPx, heightPx, caption).
Cable network honors public interfaces
The CAT cable BFS used to recognize only built-in block/BE classes. Now it consults the public interfaces directly:
- ISpcCableTraversable: blocks implementing this are traversed by the cable BFS as a final fallback after the built-in checks. No registry call needed — implementing the interface is enough.
- ISpcNetworkDevice: BEs implementing this are discovered as devices during BFS, using deviceTypeId() / ipAddress() for routing and displayName() / label() for editor presentation. Device-type IDs are resolved against SpcNetworkRegistry for display names.
- NetworkCableBlock.canConnectTo also honors ISpcCableTraversable, so addon cable-traversable blocks get visual connections too.
Virtual-signal dispatch to addon devices
ISpcExecutionContext.readVirtualDigital / readVirtualInteger / writeVirtualDigital / writeVirtualInteger now route reads and writes whose source key matches an ISpcNetworkDevice IP to that device's readDeviceValue() / applyDeviceValue(SpcSignalValue). Addon nodes can talk to their endpoint BEs purely through the virtual-signal API — no direct BE access, no reflection.
Device discovery helper
- SpcNetworkRegistry.discoverReachableDevices(Level, BlockPos) returns an immutable List<SpcReachableDevice> of every network device reachable from a source position over the cable network (built-in and addon devices alike). Server-thread safe.
- SpcReachableDevice (record): pos, ipAddress, deviceTypeId, displayName. Cache once-per-tick from ISpcExecutionContextExtension.onTickStart for compiled nodes to consume.
Per-parameter editor label override
- SpcParameterSpec gained an optional 4th component labelTranslationKey (Nullable). When set, the programming block screen resolves this lang key (trailing colon appended) instead of using the hardcoded label table for that parameterId.
- A 3-arg backwards-compatible constructor delegates to (parameterId, valueType, required, null) so older addons keep source- and binary-linking unchanged.
- Helper: SpcParameterSpec.hasLabelTranslationKey().
- Label precedence: spec.labelTranslationKey (resolved & non-empty) → built-in hardcoded switch → titleized parameterId.
DYNAMIC_CHOICE parameter type
New SpcParameterValueType value lets addon nodes declare a parameter the editor renders as a dropdown populated on-demand by a server-side provider. Useful for "pick one of the register channels of the server selected in display_ip"-style UX.
- com.hypernova.spc.api.node.SpcChoice (record): dropdown entry — (serialized, displayLabel). The serialized string is what the parameter saves; under the hood DYNAMIC_CHOICE persists like TEXT.
- com.hypernova.spc.api.node.SpcChoiceContext (interface): exposes Level, programming-block BlockPos, and the requesting player's UUID. Providers may inspect the world and filter on the player.
- com.hypernova.spc.api.node.SpcDynamicChoiceProvider (functional interface): List<SpcChoice> choicesFor(parameterId, parameterValues, ctx). Receives a snapshot of every parameter currently set on the node being edited so choice lists can depend on sibling values.
- com.hypernova.spc.api.registry.SpcDynamicChoiceRegistry: keyed by (nodeTypeId, parameterId). Static, freezable, register from the @Mod constructor. Frozen alongside the other API registries.
Runtime contract for providers:
- Run server-side. Editor sends a request with the current param map and programming block position. Server enforces a 64-block reach check, runs the provider, returns up to 256 entries. Provider RuntimeExceptions are caught and surfaced to the editor as an inline error message; the user can still type a literal value into the field.
- Providers must be cheap and read-only — they run on the server tick thread.
Per-node diagnostics entries
ISpcCompiledNode gained a default method List<SpcDiagnosticEntry> getDiagnosticEntries() called server-side once per snapshot capture, after the node has executed.
- Default returns List.of() — existing addons relink unchanged.
- LogoDiagnosticsSnapshotBuilder walks every compiled node after the ISpcDiagnosticsProvider scan and appends each addon node's entries to the addon-diagnostics map the panel renders. Built-in nodes are skipped (only addon-adapter instances are asked).
- Provider exceptions are caught and silently drop that node's entries for the current snapshot.
- Same-label collisions: last write wins (matches existing ISpcDiagnosticsProvider semantics). Embed an identifier in the label for multiple instances (e.g. "ModbusWrite1: mc_start").
Documentation: load order and server lifecycle
- com.hypernova.spc.api package-info documents the two supported registration patterns (register in @Mod constructor, or force ordering via neoforge.mods.toml dependencies with ordering = "BEFORE") and the NeoForge server-lifecycle events (ServerStartedEvent / ServerStoppingEvent / ServerStoppedEvent) for background executors and external connections.
- com.hypernova.spc.api.network package-info documents the endpoint discovery pattern: cache discoverReachableDevices results in an ISpcExecutionContextExtension.onTickStart and filter by deviceTypeId at execute() time; read/write values via the ISpcExecutionContext virtual-signal methods keyed by device IP.
Network protocol
ModNetwork PROTOCOL_VERSION bumped to 7 — added SpcChoiceRequestPayload (C→S) and SpcChoiceResponsePayload (S→C) for dynamic-choice population. (Mod 1.1.0 ships protocol 8 for the live diagnostics overlay; clients and servers must match major.)
Migration notes
- Addons that previously shipped mixins for cable connection or device discovery (e.g. spc_create / rotationalprogramcontrols NetworkCableTraversableMixin, LogoNetworkRouteFinderDeviceMixin) can delete those mixin classes and the entire mixin source tree. Re-export blocks/BEs through ISpcCableTraversable and ISpcNetworkDevice and rebuild against v6.
- For addon-specific dropdowns or labels: add labelTranslationKey to the 4-arg SpcParameterSpec ctor; switch the parameter's valueType to DYNAMIC_CHOICE and call SpcDynamicChoiceRegistry.register(nodeTypeId, parameterId, provider) from the @Mod constructor.
Internal change exposed
- AddonCompiledNodeAdapter is now public so the diagnostics builder (sibling package) can reach it. It is an implementation-detail class, not part of the addon API surface; do not depend on it from addons.
Backwards compatibility
- Additive only across the entire v6 surface. All 1.1.6 API surfaces source- and binary-link. SpcParameterSpec keeps both 3-arg and 4-arg constructors. ISpcCompiledNode.getDiagnosticEntries() defaults to an empty list.

