2025-10-29 18:03:54 +01:00
2025-10-29 14:55:11 +01:00
2025-10-29 18:03:54 +01:00
2025-10-29 18:03:54 +01:00
2025-10-29 14:55:11 +01:00
2025-10-29 17:08:31 +01:00
2025-10-29 17:41:33 +01:00
2025-10-29 14:55:11 +01:00
2025-10-29 18:03:54 +01:00
2025-10-29 14:55:11 +01:00

oc2r-rust

Thin Rust bindings for the OC2R HLAPI controller. The crate mirrors the behaviour of the Lua devices.lua helper: it speaks JSON over the first VirtIO console (/dev/hvc0) and exposes ergonomic helpers for enumerating devices, inspecting their methods, and invoking them.

Status

  • mirrors devices.lua: list, get, find, find_all, methods, invoke
  • Device wrapper with lazy method cache
  • JsonValue alias you can use to build invocation parameters
  • event subscription (subscribe / unsubscribe) not exposed yet

The low-level framing logic follows the Java implementation in RPCDeviceBusAdapter: messages are framed with leading/trailing NUL bytes and the console is switched into raw mode before use.

Using the library

Add the crate to your workspace and interact with it directly:

use oc2r_rust::{DeviceBus, Result, DEFAULT_DEVICE_PATH};

fn main() -> Result<()> {
    let mut bus = DeviceBus::connect(DEFAULT_DEVICE_PATH)?;

    for device in bus.list()? {
        println!("deviceId: {}", device.device_id);
        println!("  types: {}", device.type_names.join(", "));
    }

    // Invoke a method on the first device as an example.
    if let Some(mut device) = bus.device("some-device-id")? {
        let response = device.call("help", ())?;
        println!("help(): {response:?}");
    }

    Ok(())
}

Dynamic method calls

Device::call (and the matching DeviceBus::call) accept anything that implements IntoJsonArgs, so you can stay close to the Lua ergonomics:

device.call("setRedstoneOutput", ("east", 15_u8))?;
device.call("getRedstoneInput", ("north",))?;

Typed helpers

For common peripherals you can rely on typed wrappers. The redstone helper mirrors the behaviour of redstone.lua and performs basic clamping/parsing:

use oc2r_rust::devices::redstone::{Redstone, Side};
use oc2r_rust::{DeviceBus, Result, DEFAULT_DEVICE_PATH};

fn main() -> Result<()> {
    let mut bus = DeviceBus::connect(DEFAULT_DEVICE_PATH)?;
    let mut redstone = Redstone::attach(&mut bus)?
        .expect("no redstone interface attached");

    redstone.set_output_state(Side::East, true)?;
    println!("in: {}", redstone.input(Side::East)?);
    Ok(())
}

Building the examples

Two runnable examples live in examples/:

  • list-devices enumerate devices and pretty-print their metadata
  • invoke-method list available methods for the first device and invoke one
  • redstone toggle/query a redstone interface, mirroring redstone.lua

The OC2R VM does not ship a Rust toolchain, so build the binaries on your host and copy them into the VM:

# once
cargo install cross --git https://github.com/cross-rs/cross

# build release binaries
cross build --release --example list-devices
cross build --release --example invoke-method
cross build --release --example redstone

The resulting executables live under target/<triple>/release/examples/. Copy them into Minux using your preferred workflow—an Import/Export card in Minecraft works well if you drop the binary onto the card outside the VM. Once the file is on disk (e.g. /usr/bin/), you can execute it from the shell.

Architecture overview

  • bus.rs high-level DeviceBus/Device helpers
  • transport.rs POSIX plumbing (termios, poll, read/write)
  • rpc.rs miniserde data structures for OC2R messages
  • error.rs shared error type and Result<T> alias

DeviceBus flushes the descriptor before sending each request, matching the behaviour of both Lua and Java implementations and preventing stale replies from leaking into new invocations.

Description
No description provided
Readme 233 KiB
Languages
Rust 99.7%
Shell 0.3%