Peripherals and networks

A computer can reach devices directly on its six faces or through a remotely bound peripheral network. A device API belongs to the integration that provides it: displays and Redstone Ports are built into Compukters, while Create and Propulsion devices need their corresponding addons.

Connect a device

For a directly adjacent device, use Device.at(Side.front) or the API’s existing side helper. Sides are relative to the computer’s front face: front, back, left, right, top and bottom.

For a remote connection, use the Peripheral Configurator:

  1. Hold RMB, point at Network binding in the radial menu, then release RMB. A short click performs the selected action; Escape or the center of the menu cancels mode selection.
  2. Click an unbound computer or device to create a network. Clicking an already bound member selects its network.
  3. Click other computers and devices to join them to the selected network. The selected configurator glows. Unnamed devices receive a name such as display_1 or speedometer_1. The first free number is selected within this network; existing names are preserved. Names use the technical device kind and are independent of game language.
  4. Hold RMB and select Naming to return to naming mode. Click a peripheral to give it a name, or click a computer to rename the network and inspect its members. The inspector shows unavailable devices and lets you remove members.

Shift-use in the air clears the selected network. In network mode, Shift-click a member to unbind it; in naming mode, Shift-click a peripheral to clear its name. A member belongs to one network at a time; unbind it before joining a different network. Names must be unique within the network.

The network persists independently of its computers. After replacing a computer, select the existing network by clicking one of its peripherals, then bind the new computer. Other members keep their bindings and names. A replacement peripheral does not normally inherit the removed block’s membership. A matching display panel placed inside an existing screen automatically rejoins that screen and its network; see Display.

Each computer can reach loaded peripherals in the same dimension within 64 blocks of itself, measured in three dimensions. The server can configure this through peripherals.access_radius (1–1,024 blocks). Joining a distant member is allowed: a network can extend arbitrarily far, but each computer has its own accessible subset. No chunks are force-loaded. With Compukters: Sable, distance follows the current construction position, rotation and scale. The same network can connect devices on shore and on separate constructions. Assembly and return retain bindings and names; no special Guest API is needed.

Cable blocks are now Computer Cables, reserved for physical connections between computers. Place a continuous line between two computers; the line can turn or run vertically, but cannot branch or loop. A computer accepts one adjacent cable. Invalid placement is rejected with a message and keeps the item in your hand. Old branched constructions remain in the world but provide no computer link. Links require the entire line and both computers to be loaded.

Cables do not participate in peripheral discovery. Bind peripherals with the configurator; old cable connections are not migrated automatically. Direct adjacent access still works without binding.

For example, open a named display:

import compukter.display.TextDisplay

fun main() {
    val screen = TextDisplay.named("panel")
    screen.writeAt(0, 0, "Connected")
}

The published image remains visible after the program exits. An adjacent display can instead be opened with TextDisplay.at(Side.front). Follow the display guide for bounds and output ownership.

Send messages between computers

Connect two powered-on computers with one Computer Cable line. The cable determines the peer; no name, address, modem or configurator binding is required. Both computers and the entire cable must be loaded in the same world.

On the first computer:

import compukter.network.Network

fun main() {
    if (!Network.connected) return
    Network.send(byteArrayOf(0, -128, -1))
    val reply = Network.receive()
    println(reply.size)
}

On the second computer:

import compukter.network.Network

fun main() {
    val message = Network.receive()
    Network.send(message)
}

send copies one complete message into the peer’s inbox. receive waits for one message without blocking the server. Each inbox holds up to 16 messages, each at most 4096 bytes; empty messages are valid. The world also has a shared 1 MiB queued-byte budget. A full inbox or unavailable connection throws compukter.io.IOException; applications can catch it and retry later. Oversized Guest arguments are rejected by the VM’s host-request quota before dispatch. Programs on one computer share its inbox and consume messages in arrival order.

Cutting the cable, replacing or powering off a computer, or unloading a participating chunk discards queued messages and interrupts waiting receives. Repairing the cable creates a fresh connection. Queues are transient and are not saved across hibernation or server restarts; a restored waiting receive reports interruption. Messages already accepted by send are queued, without a delivery acknowledgment. Peripheral bindings remain separate from this cable connection.

Typed discovery

Device classes also act as providers. For example, TextDisplay, RedstonePort, Create’s Speedometer, Stressometer, RotationController, Pump, Clutch, StockTicker and Boiler, and Propulsion’s CreativeThruster and CreativeVectorThruster share these methods:

Method Result
first() / first { predicate } First matching device; throws NoSuchElementException if absent
firstOrNull() / firstOrNull { predicate } First matching device, or null
all() / filter { predicate } List of reachable devices, optionally filtered
at(Side.front) / atOrNull(Side.front) One device on that relative computer face
named("name") / namedOrNull("name") One uniquely named reachable device
import create.kinetics.Speedometer
import compukter.peripheral.Side

fun main() {
    val moving = Speedometer.firstOrNull { it.speed() != 0f }
    println(moving?.speed())
    for (gauge in Speedometer.filter { it.speed() > 64f }) {
        println(gauge.speed())
    }
    println(Speedometer.atOrNull(Side.top)?.speed())
}

Discovery follows a deterministic order by device anchor coordinates, addon id and logical device key. first and firstOrNull stop as soon as the predicate matches. A predicate receives the typed device and may call its operations. Snapshot resources are released after a normal return, an early match or a thrown predicate. Optional methods return null for absence; invalid names, ambiguous names, wrong device types and resource limits remain errors. Missing strict side/name selections throw NoSuchElementException.

The computer retains at most 1,024 typed handles across all integrations, with at most four open discovery snapshots and 1,024 entries per snapshot. Discovery stays in loaded chunks. In the IDE, provider values have a dedicated color and a P completion badge; the same name used as a device type keeps ordinary class presentation. Device wrappers are value classes implementing Peripheral: direct device calls use scalar handles, while nullable values, interface references and collections use typed managed wrappers. The old Display, Kinetics, Boilers, Logistics, Thrusters helpers and their separate side types are removed. Use typed providers and the common Side instead. Create uses addon API 2.1; Propulsion uses API 2.0. Rebuild dependent Guest bundles and programs against the updated platform; exact bundle versions and hashes still govern compiled artifacts.

One program owns a device

Acquiring a typed device reserves it exclusively for that running program, including read-only use. Another program receives compukter.io.IOException while it is busy, even on the same computer. VM tasks inside one program share its handles; a child process has its own ownership and cannot borrow its suspended parent’s device.

Discovery snapshots do not reserve unread devices. Selecting entries, including entries passed to predicates, acquires handles. The program retains these handles until its device API closes them, the program exits, or connectivity is lost. A second typed view of the same device does not bypass ownership. After hibernation, restoring a persistent handle requires the device to be reachable and available again.

Device handles have a lifetime

A handle refers to the device you acquired, not whichever block later occupies its position. Removing, replacing, unloading or disconnecting a device can invalidate it. Reconnect and acquire a new handle instead of assuming an old one will retarget. An expired typed handle fails with compukter.io.IOException; it never revives after reconnection. Control-release behavior depends on the device API. Hibernation can rebind a retained handle when the provider supplies a persistent instance identity and the same device is reachable on wake. Previously observed stale handles remain stale; an address alone never identifies a replacement device.

Device Guide
Redstone Port Redstone
Text display Display
Create gauges and rotation controller Kinetics
Create Stock Ticker Logistics
Create steam boiler Boilers
Propulsion creative engines Propulsion

While holding the configurator in either hand, nearby configurable devices receive softly pulsing outlines. Members of a bound network share a color; hovering a member or selecting the network emphasizes the whole nearby group. Outlines are hidden behind blocks. The crosshair panel shows the current mode, target name, network and action hints. Use the item in the air to cycle modes, or shift-use it in the air to clear the selection. Display assembly also shows screen boundaries, holes and unjoined replacement panels; see Display.