From 0faa775fea33666f26c5bc254ed41f448dd7d3e8 Mon Sep 17 00:00:00 2001 From: TheBlckbird Date: Thu, 28 Aug 2025 11:15:21 +0200 Subject: [PATCH 1/4] Add rust crate --- rustedcomputer-rs/.gitignore | 2 ++ rustedcomputer-rs/Cargo.lock | 7 +++++++ rustedcomputer-rs/Cargo.toml | 11 +++++++++++ rustedcomputer-rs/Readme.md | 3 +++ rustedcomputer-rs/src/lib.rs | 14 ++++++++++++++ 5 files changed, 37 insertions(+) create mode 100644 rustedcomputer-rs/.gitignore create mode 100644 rustedcomputer-rs/Cargo.lock create mode 100644 rustedcomputer-rs/Cargo.toml create mode 100644 rustedcomputer-rs/Readme.md create mode 100644 rustedcomputer-rs/src/lib.rs diff --git a/rustedcomputer-rs/.gitignore b/rustedcomputer-rs/.gitignore new file mode 100644 index 0000000..f2564bc --- /dev/null +++ b/rustedcomputer-rs/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +target/ diff --git a/rustedcomputer-rs/Cargo.lock b/rustedcomputer-rs/Cargo.lock new file mode 100644 index 0000000..313d475 --- /dev/null +++ b/rustedcomputer-rs/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "rustedcomputer" +version = "0.1.0" diff --git a/rustedcomputer-rs/Cargo.toml b/rustedcomputer-rs/Cargo.toml new file mode 100644 index 0000000..ad8d25d --- /dev/null +++ b/rustedcomputer-rs/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rustedcomputer" +version = "0.1.0" +edition = "2024" +license = "MPL-2.0" +description = "Programmable Computers in Minecraft with WebAssembly" +homepage = "https://github.com/TheBlckbird/rustedcomputer" +repository = "https://github.com/TheBlckbird/rustedcomputer" +readme = "Readme.md" + +[dependencies] diff --git a/rustedcomputer-rs/Readme.md b/rustedcomputer-rs/Readme.md new file mode 100644 index 0000000..62a5b74 --- /dev/null +++ b/rustedcomputer-rs/Readme.md @@ -0,0 +1,3 @@ +# Rusted Computer + +This is a reserved crate. It will later be the wrapper crate for [github.com/TheBlckbird/rustedcomputer](https://github.com/TheBlckbird/rustedcomputer). diff --git a/rustedcomputer-rs/src/lib.rs b/rustedcomputer-rs/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/rustedcomputer-rs/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 73392b9cacb7975f09dc0e5556c9b15e4fed84e4 Mon Sep 17 00:00:00 2001 From: TheBlckbird Date: Sat, 30 Aug 2025 22:50:26 +0200 Subject: [PATCH 2/4] Add futures --- .gitignore | 4 +- .../computer/block/ComputerBlockEntity.kt | 11 ++- .../computer/hostfunctions/HostFunctions.kt | 21 ------ .../hostfunctions/HostFunctionsHelpers.kt | 29 ++++++++ .../hostfunctions/infrastructure/Command.kt | 7 ++ .../hostfunctions/infrastructure/Commands.kt | 74 +++++++++++++++++++ .../hostfunctions/infrastructure/Future.kt | 20 +++++ .../infrastructure/FutureFunctions.kt | 36 +++++++++ .../redstone/RedstoneFunctions.kt | 56 ++++++++++++++ .../redstone/SetRedstoneOutput.kt | 39 ++++++++++ 10 files changed, 271 insertions(+), 26 deletions(-) delete mode 100644 src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/HostFunctions.kt create mode 100644 src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/HostFunctionsHelpers.kt create mode 100644 src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Command.kt create mode 100644 src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Commands.kt create mode 100644 src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Future.kt create mode 100644 src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/FutureFunctions.kt create mode 100644 src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/RedstoneFunctions.kt create mode 100644 src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/SetRedstoneOutput.kt diff --git a/.gitignore b/.gitignore index 31d2550..6d6048b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ run runs run-data -repo \ No newline at end of file +repo + +.DS_Store \ No newline at end of file diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/block/ComputerBlockEntity.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/block/ComputerBlockEntity.kt index 1d09127..5efa827 100644 --- a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/block/ComputerBlockEntity.kt +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/block/ComputerBlockEntity.kt @@ -10,8 +10,9 @@ import dev.theblckbird.rustedcomputer.RustedComputer import dev.theblckbird.rustedcomputer.RelativeDirection import dev.theblckbird.rustedcomputer.computer.ComputerObservations import dev.theblckbird.rustedcomputer.computer.ComputerScreenHolder -import dev.theblckbird.rustedcomputer.computer.hostfunctions.HostFunctions import dev.theblckbird.rustedcomputer.computer.MinecraftTimeClock +import dev.theblckbird.rustedcomputer.computer.hostfunctions.infrastructure.FutureFunctions +import dev.theblckbird.rustedcomputer.computer.hostfunctions.redstone.RedstoneFunctions import dev.theblckbird.rustedcomputer.computer.networking.toclient.stdout.StdoutData import dev.theblckbird.rustedcomputer.helpers.SaveFileHelper import kotlinx.coroutines.CoroutineScope @@ -104,11 +105,13 @@ class ComputerBlockEntity(position: BlockPos, state: BlockState) : val wasi = WasiPreview1.builder().withOptions(options).build() - val hostFunctions = HostFunctions() + val redstoneFunctions = RedstoneFunctions(level, blockPos) + val futureFunctions = FutureFunctions() val store = Store() .addFunction(*wasi.toHostFunctions()) - .addFunction(*hostFunctions.toHostFunctions()) + .addFunction(*redstoneFunctions.toHostFunctions()) + .addFunction(*futureFunctions.toHostFunctions()) val resourceManager = level.server.resourceManager val romFileLocation = ResourceLocation.fromNamespaceAndPath(RustedComputer.MODID, "rom/$fileName") @@ -132,7 +135,7 @@ class ComputerBlockEntity(position: BlockPos, state: BlockState) : ) } } else { - writelnStdout("Couldn't find file $fileName") + writelnStdout("Can't find file $fileName") } } diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/HostFunctions.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/HostFunctions.kt deleted file mode 100644 index 69223f9..0000000 --- a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/HostFunctions.kt +++ /dev/null @@ -1,21 +0,0 @@ -package dev.theblckbird.rustedcomputer.computer.hostfunctions - -import com.dylibso.chicory.annotations.HostModule -import com.dylibso.chicory.annotations.WasmExport -import com.dylibso.chicory.runtime.HostFunction -import dev.theblckbird.rustedcomputer.RustedComputer - -@HostModule("rustedcomputer") -final class HostFunctions { - /** - * This is kept in as an easter egg - */ - @WasmExport - fun tom() { - RustedComputer.Companion.LOGGER.info("Tooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooomm") - } - - fun toHostFunctions(): Array { - return HostFunctions_ModuleFactory.toHostFunctions(this) - } -} \ No newline at end of file diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/HostFunctionsHelpers.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/HostFunctionsHelpers.kt new file mode 100644 index 0000000..65ad25d --- /dev/null +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/HostFunctionsHelpers.kt @@ -0,0 +1,29 @@ +package dev.theblckbird.rustedcomputer.computer.hostfunctions + +import com.dylibso.chicory.runtime.ExportFunction +import com.dylibso.chicory.runtime.Memory +import dev.theblckbird.rustedcomputer.RustedComputer + +object HostFunctionsHelpers { + /** + * Combines a pointer (u32, represented as an `Int` so it can be passed to WASM) and a length to a `Long`. + * + * @return The pointer and length combined + */ + fun combinePointerAndLength(pointer: Int, length: Int): Long { + return (pointer.toLong() shl 32) or (length.toLong() and 0xFFFFFFFFL) + } + + /** + * Allocates a string in WASM memory and returns the combined pointer and length. + * + * @return The pointer and length combined to a singular `Long` + */ + fun allocateString(content: String, alloc: ExportFunction, memory: Memory): Long { + val length = content.toByteArray().count() + val pointer = alloc.apply(length.toLong())[0].toInt() + memory.writeString(pointer, content) + + return combinePointerAndLength(pointer, length) + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Command.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Command.kt new file mode 100644 index 0000000..2b4dd68 --- /dev/null +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Command.kt @@ -0,0 +1,7 @@ +package dev.theblckbird.rustedcomputer.computer.hostfunctions.infrastructure + +import net.minecraft.server.level.ServerLevel + +interface Command { + fun run(level: ServerLevel): String +} \ No newline at end of file diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Commands.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Commands.kt new file mode 100644 index 0000000..28dffec --- /dev/null +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Commands.kt @@ -0,0 +1,74 @@ +package dev.theblckbird.rustedcomputer.computer.hostfunctions.infrastructure + +import dev.theblckbird.rustedcomputer.RustedComputer +import net.minecraft.world.level.Level +import net.neoforged.bus.api.SubscribeEvent +import net.neoforged.fml.common.EventBusSubscriber +import net.neoforged.neoforge.event.tick.ServerTickEvent +import java.util.LinkedList +import java.util.Queue + +typealias FutureId = Int + +@EventBusSubscriber(modid = RustedComputer.MODID) +object Commands { + private var commands: Queue> = LinkedList() + private var latestFutureId = 0 + + /** + * A list of all the futures that completed in the last tick. + * + * This will be cleaned up in the amount of ticks the integer specifies. + */ + private var completedFutures = hashMapOf>() + + @SubscribeEvent + fun runAllCommands(event: ServerTickEvent.Pre) { + for (completedFuture in completedFutures) { + if (completedFuture.value.second == 0) { + completedFutures.remove(completedFuture.key) + } else { + val (result, ticksRemaining) = completedFuture.value + completedFuture.setValue(result to ticksRemaining - 1) + } + } + + var command: Pair? + + while (commands.isNotEmpty()) { + command = commands.peek() + val result = command.second.run(event.server.getLevel(Level.OVERWORLD)!!) + completedFutures[command.first] = result to 5 + commands.remove() + } + } + + /** + * Pushes a command to the queue + * + * @return The future id needed to poll the result + */ + fun pushCommand(command: Command): Int { + latestFutureId += 1 + commands.add(latestFutureId to command) + + return latestFutureId + } + + /** + * Gets the future for a specific id. + * + * @return The future or `null` if it doesn't exist. + */ + fun getFuture(futureId: FutureId): Future? { + val completedFuture = completedFutures.remove(futureId) + + return if (completedFuture != null) { + Future.Success(completedFuture.first) + } else if (commands.any { it.first == futureId }) { + Future.Pending + } else { + null + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Future.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Future.kt new file mode 100644 index 0000000..83f05b4 --- /dev/null +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/Future.kt @@ -0,0 +1,20 @@ +package dev.theblckbird.rustedcomputer.computer.hostfunctions.infrastructure + +sealed class Future { + object Pending : Future() + object Failed : Future() + + data class Success( + val content: String + ) : Future() { + override fun toString(): String = "S$content" + } + + override fun toString(): String { + return when(this) { + Failed -> "F" + Pending -> "P" + is Success -> "S" // handled by the data class itself + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/FutureFunctions.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/FutureFunctions.kt new file mode 100644 index 0000000..5d37067 --- /dev/null +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/infrastructure/FutureFunctions.kt @@ -0,0 +1,36 @@ +package dev.theblckbird.rustedcomputer.computer.hostfunctions.infrastructure + +import com.dylibso.chicory.annotations.HostModule +import com.dylibso.chicory.annotations.WasmExport +import com.dylibso.chicory.runtime.HostFunction +import com.dylibso.chicory.runtime.Instance +import com.dylibso.chicory.runtime.Memory +import dev.theblckbird.rustedcomputer.computer.hostfunctions.HostFunctionsHelpers + +/** + * Host functions for polling futures + */ +@HostModule("future") +class FutureFunctions { + var gehalt = 3 + set(value) { + if (value >= 0) gehalt = value + } + + /** + * The client program has to deallocate the used memory. + */ + @WasmExport + fun poll(memory: Memory, instance: Instance, futureId: FutureId): Long { + val future = Commands.getFuture(futureId) + val stringifiedFuture = future.toString() + val alloc = instance.export("alloc") + val address = HostFunctionsHelpers.allocateString(stringifiedFuture, alloc, memory) + + return address + } + + fun toHostFunctions(): Array { + return FutureFunctions_ModuleFactory.toHostFunctions(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/RedstoneFunctions.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/RedstoneFunctions.kt new file mode 100644 index 0000000..1e951fe --- /dev/null +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/RedstoneFunctions.kt @@ -0,0 +1,56 @@ +package dev.theblckbird.rustedcomputer.computer.hostfunctions.redstone + +import com.dylibso.chicory.annotations.HostModule +import com.dylibso.chicory.annotations.WasmExport +import com.dylibso.chicory.runtime.HostFunction +import com.dylibso.chicory.runtime.Memory +import dev.theblckbird.rustedcomputer.RelativeDirection +import dev.theblckbird.rustedcomputer.computer.hostfunctions.infrastructure.Commands +import net.minecraft.core.BlockPos +import net.minecraft.core.Direction +import net.minecraft.server.level.ServerLevel +import net.minecraft.world.level.block.HorizontalDirectionalBlock + +@HostModule("redstone") +class RedstoneFunctions(val level: ServerLevel, val computerPosition: BlockPos) { + @WasmExport + fun setOutput(memory: Memory, sideLength: Int, sideOffset: Int, power: Int): Int { + val side = memory.readString(sideOffset, sideLength) + return Commands.pushCommand(SetRedstoneOutput(side, power, computerPosition)) + } + + @WasmExport + fun getInput(memory: Memory, sideLength: Int, sideOffset: Int): Int { + val side = memory.readString(sideOffset, sideLength) + + val powerSide = when (side) { + "top" -> RelativeDirection.TOP + "bottom" -> RelativeDirection.BOTTOM + "left" -> RelativeDirection.LEFT + "right" -> RelativeDirection.RIGHT + "front" -> RelativeDirection.FRONT + "back" -> RelativeDirection.BACK + else -> null + } + + val absoluteDirection = getAbsoluteDirection(level.getBlockState(computerPosition).getValue( + HorizontalDirectionalBlock.FACING), powerSide!!) + + return level.getSignal(computerPosition, absoluteDirection) + } + + fun getAbsoluteDirection(blockFacing: Direction, relativeDirection: RelativeDirection): Direction { + return when (relativeDirection) { + RelativeDirection.TOP -> Direction.UP + RelativeDirection.BOTTOM -> Direction.DOWN + RelativeDirection.LEFT -> blockFacing.counterClockWise + RelativeDirection.RIGHT -> blockFacing.clockWise + RelativeDirection.FRONT -> blockFacing + RelativeDirection.BACK -> blockFacing.opposite + } + } + + fun toHostFunctions(): Array { + return RedstoneFunctions_ModuleFactory.toHostFunctions(this) + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/SetRedstoneOutput.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/SetRedstoneOutput.kt new file mode 100644 index 0000000..c2736f1 --- /dev/null +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/SetRedstoneOutput.kt @@ -0,0 +1,39 @@ +package dev.theblckbird.rustedcomputer.computer.hostfunctions.redstone + +import dev.theblckbird.rustedcomputer.ModBlocks +import dev.theblckbird.rustedcomputer.RelativeDirection +import dev.theblckbird.rustedcomputer.computer.block.ComputerBlock +import dev.theblckbird.rustedcomputer.computer.hostfunctions.infrastructure.Command +import net.minecraft.core.BlockPos +import net.minecraft.server.level.ServerLevel +import net.minecraft.world.level.block.Block + +class SetRedstoneOutput( + private val side: String, + private val power: Int, + private val computerPosition: BlockPos +) : Command { + override fun run(level: ServerLevel): String { + val blockState = level.getBlockState(computerPosition) + + val computer = ComputerBlock.getBlockEntity(level, computerPosition) + + if (computer == null) { + return null.toString() + } + + when (side) { + "top" -> computer.powerLevels[RelativeDirection.TOP] = power + "bottom" -> computer.powerLevels[RelativeDirection.BOTTOM] = power + "left" -> computer.powerLevels[RelativeDirection.LEFT] = power + "right" -> computer.powerLevels[RelativeDirection.RIGHT] = power + "front" -> computer.powerLevels[RelativeDirection.FRONT] = power + "back" -> computer.powerLevels[RelativeDirection.BACK] = power + } + + level.setBlock(computerPosition, blockState, Block.UPDATE_NEIGHBORS) + level.updateNeighborsAt(computerPosition, ModBlocks.COMPUTER.get()) + + return null.toString() + } +} \ No newline at end of file From bb1c00a8da9f17dfba9f01a99800ee9b3281c423 Mon Sep 17 00:00:00 2001 From: TheBlckbird Date: Mon, 1 Sep 2025 10:25:18 +0200 Subject: [PATCH 3/4] Add redstone functions --- .../rustedcomputer/RelativeDirection.kt | 12 ++++++ .../redstone/GetRedstoneInput.kt | 40 +++++++++++++++++++ .../redstone/RedstoneFunctions.kt | 30 +------------- .../redstone/SetRedstoneOutput.kt | 1 - 4 files changed, 53 insertions(+), 30 deletions(-) create mode 100644 src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/GetRedstoneInput.kt diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/RelativeDirection.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/RelativeDirection.kt index 8a28f99..238ed5b 100644 --- a/src/main/kotlin/dev/theblckbird/rustedcomputer/RelativeDirection.kt +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/RelativeDirection.kt @@ -20,6 +20,18 @@ enum class RelativeDirection { else -> null } } + + fun fromString(string: String): RelativeDirection? { + return when(string.lowercase()) { + "top" -> TOP + "bottom" -> BOTTOM + "left" -> LEFT + "right" -> RIGHT + "front" -> FRONT + "back" -> BACK + else -> null + } + } } fun toInt(): Int { diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/GetRedstoneInput.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/GetRedstoneInput.kt new file mode 100644 index 0000000..0e5701f --- /dev/null +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/GetRedstoneInput.kt @@ -0,0 +1,40 @@ +package dev.theblckbird.rustedcomputer.computer.hostfunctions.redstone + +import dev.theblckbird.rustedcomputer.RelativeDirection +import dev.theblckbird.rustedcomputer.computer.hostfunctions.infrastructure.Command +import net.minecraft.core.BlockPos +import net.minecraft.core.Direction +import net.minecraft.server.level.ServerLevel +import net.minecraft.world.level.block.HorizontalDirectionalBlock + +class GetRedstoneInput( + private val side: String, + private val computerPosition: BlockPos +) : Command { + override fun run(level: ServerLevel): String { + val relativeSide = RelativeDirection.fromString(side) + + if (relativeSide == null) { + return null.toString() + } + + val absoluteDirection = getAbsoluteDirection( + level.getBlockState(computerPosition).getValue( + HorizontalDirectionalBlock.FACING + ), relativeSide + ) + + return level.getSignal(computerPosition, absoluteDirection).toString() + } + + fun getAbsoluteDirection(blockFacing: Direction, relativeDirection: RelativeDirection): Direction { + return when (relativeDirection) { + RelativeDirection.TOP -> Direction.UP + RelativeDirection.BOTTOM -> Direction.DOWN + RelativeDirection.LEFT -> blockFacing.counterClockWise + RelativeDirection.RIGHT -> blockFacing.clockWise + RelativeDirection.FRONT -> blockFacing + RelativeDirection.BACK -> blockFacing.opposite + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/RedstoneFunctions.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/RedstoneFunctions.kt index 1e951fe..44fdbe3 100644 --- a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/RedstoneFunctions.kt +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/RedstoneFunctions.kt @@ -4,12 +4,9 @@ import com.dylibso.chicory.annotations.HostModule import com.dylibso.chicory.annotations.WasmExport import com.dylibso.chicory.runtime.HostFunction import com.dylibso.chicory.runtime.Memory -import dev.theblckbird.rustedcomputer.RelativeDirection import dev.theblckbird.rustedcomputer.computer.hostfunctions.infrastructure.Commands import net.minecraft.core.BlockPos -import net.minecraft.core.Direction import net.minecraft.server.level.ServerLevel -import net.minecraft.world.level.block.HorizontalDirectionalBlock @HostModule("redstone") class RedstoneFunctions(val level: ServerLevel, val computerPosition: BlockPos) { @@ -22,32 +19,7 @@ class RedstoneFunctions(val level: ServerLevel, val computerPosition: BlockPos) @WasmExport fun getInput(memory: Memory, sideLength: Int, sideOffset: Int): Int { val side = memory.readString(sideOffset, sideLength) - - val powerSide = when (side) { - "top" -> RelativeDirection.TOP - "bottom" -> RelativeDirection.BOTTOM - "left" -> RelativeDirection.LEFT - "right" -> RelativeDirection.RIGHT - "front" -> RelativeDirection.FRONT - "back" -> RelativeDirection.BACK - else -> null - } - - val absoluteDirection = getAbsoluteDirection(level.getBlockState(computerPosition).getValue( - HorizontalDirectionalBlock.FACING), powerSide!!) - - return level.getSignal(computerPosition, absoluteDirection) - } - - fun getAbsoluteDirection(blockFacing: Direction, relativeDirection: RelativeDirection): Direction { - return when (relativeDirection) { - RelativeDirection.TOP -> Direction.UP - RelativeDirection.BOTTOM -> Direction.DOWN - RelativeDirection.LEFT -> blockFacing.counterClockWise - RelativeDirection.RIGHT -> blockFacing.clockWise - RelativeDirection.FRONT -> blockFacing - RelativeDirection.BACK -> blockFacing.opposite - } + return Commands.pushCommand(GetRedstoneInput(side, computerPosition)) } fun toHostFunctions(): Array { diff --git a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/SetRedstoneOutput.kt b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/SetRedstoneOutput.kt index c2736f1..36e0548 100644 --- a/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/SetRedstoneOutput.kt +++ b/src/main/kotlin/dev/theblckbird/rustedcomputer/computer/hostfunctions/redstone/SetRedstoneOutput.kt @@ -15,7 +15,6 @@ class SetRedstoneOutput( ) : Command { override fun run(level: ServerLevel): String { val blockState = level.getBlockState(computerPosition) - val computer = ComputerBlock.getBlockEntity(level, computerPosition) if (computer == null) { From 8d96b857ebbd93bd735227856cec3ae00af8d9ad Mon Sep 17 00:00:00 2001 From: TheBlckbird Date: Mon, 1 Sep 2025 16:47:49 +0200 Subject: [PATCH 4/4] Update rust crate --- rustedcomputer-rs/Cargo.lock | 58 +++++++++++ rustedcomputer-rs/Cargo.toml | 1 + .../src/async_runtime/future_id.rs | 23 +++++ rustedcomputer-rs/src/async_runtime/mod.rs | 95 +++++++++++++++++++ rustedcomputer-rs/src/error.rs | 11 +++ rustedcomputer-rs/src/functions/mod.rs | 1 + rustedcomputer-rs/src/functions/redstone.rs | 34 +++++++ rustedcomputer-rs/src/lib.rs | 21 ++-- rustedcomputer-rs/src/side.rs | 25 +++++ 9 files changed, 257 insertions(+), 12 deletions(-) create mode 100644 rustedcomputer-rs/src/async_runtime/future_id.rs create mode 100644 rustedcomputer-rs/src/async_runtime/mod.rs create mode 100644 rustedcomputer-rs/src/error.rs create mode 100644 rustedcomputer-rs/src/functions/mod.rs create mode 100644 rustedcomputer-rs/src/functions/redstone.rs create mode 100644 rustedcomputer-rs/src/side.rs diff --git a/rustedcomputer-rs/Cargo.lock b/rustedcomputer-rs/Cargo.lock index 313d475..dc12c3f 100644 --- a/rustedcomputer-rs/Cargo.lock +++ b/rustedcomputer-rs/Cargo.lock @@ -2,6 +2,64 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rustedcomputer" version = "0.1.0" +dependencies = [ + "thiserror", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/rustedcomputer-rs/Cargo.toml b/rustedcomputer-rs/Cargo.toml index ad8d25d..d7a65f6 100644 --- a/rustedcomputer-rs/Cargo.toml +++ b/rustedcomputer-rs/Cargo.toml @@ -9,3 +9,4 @@ repository = "https://github.com/TheBlckbird/rustedcomputer" readme = "Readme.md" [dependencies] +thiserror = "2.0.16" diff --git a/rustedcomputer-rs/src/async_runtime/future_id.rs b/rustedcomputer-rs/src/async_runtime/future_id.rs new file mode 100644 index 0000000..0f9c273 --- /dev/null +++ b/rustedcomputer-rs/src/async_runtime/future_id.rs @@ -0,0 +1,23 @@ +use std::ops::{Deref, DerefMut}; + +pub struct FutureId(i32); + +impl From for FutureId { + fn from(value: i32) -> Self { + Self(value) + } +} + +impl Deref for FutureId { + type Target = i32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for FutureId { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} \ No newline at end of file diff --git a/rustedcomputer-rs/src/async_runtime/mod.rs b/rustedcomputer-rs/src/async_runtime/mod.rs new file mode 100644 index 0000000..a5d016c --- /dev/null +++ b/rustedcomputer-rs/src/async_runtime/mod.rs @@ -0,0 +1,95 @@ +use std::pin::Pin; +use std::{ptr, slice, thread}; +use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; +use std::time::Duration; +use crate::async_runtime::future_id::FutureId; +use crate::error::{Result, RustedComputerError}; + +pub mod future_id; + +pub struct RustedComputerFuture +where + F: Fn(String) -> T, +{ + pub id: FutureId, + pub converter: F, +} + +impl RustedComputerFuture +where + F: Fn(String) -> T, +{ + pub fn new(caller: G, converter: F) -> Self + where + G: Fn() -> FutureId, + { + let id = caller(); + Self { id, converter } + } +} + +impl Future for RustedComputerFuture +where + F: Fn(String) -> T, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + let poll_result = unsafe { poll(*self.id) }; + + let length = (poll_result & 0xFFFFFFFF) as usize; + let pointer = (poll_result >> 32) as *mut u8; + + let slice = unsafe { slice::from_raw_parts_mut(pointer, length) }; + let boxed_slice = unsafe { Box::from_raw(slice) }; + let future = str::from_utf8(&boxed_slice).unwrap(); + + if future == "P" { + Poll::Pending + } else if future == "F" { + Poll::Ready(Err(RustedComputerError::FutureFailed)) + } else if future.starts_with("S") { + let future_result = future[1..].to_owned(); + let converted_result = (self.converter)(future_result); + + Poll::Ready(Ok(converted_result)) + } else { + unreachable!() + } + } +} + +fn noop_raw_waker() -> RawWaker { + fn clone(_: *const ()) -> RawWaker { + noop_raw_waker() + } + fn no_op(_: *const ()) {} + + static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, no_op, no_op, no_op); + RawWaker::new(ptr::null(), &VTABLE) +} + +fn noop_waker() -> Waker { + unsafe { Waker::from_raw(noop_raw_waker()) } +} + +pub fn block_on(mut future: F) -> F::Output { + let mut future = unsafe { Pin::new_unchecked(&mut future) }; + let waker = noop_waker(); + let mut cx = Context::from_waker(&waker); + + loop { + match future.as_mut().poll(&mut cx) { + Poll::Ready(val) => return val, + Poll::Pending => { + thread::sleep(Duration::from_millis(100)); + } + } + } +} + +#[link(wasm_import_module = "future")] +unsafe extern "C" { + #[link_name = "poll"] + pub fn poll(future_id: i32) -> i64; +} diff --git a/rustedcomputer-rs/src/error.rs b/rustedcomputer-rs/src/error.rs new file mode 100644 index 0000000..af2a4c5 --- /dev/null +++ b/rustedcomputer-rs/src/error.rs @@ -0,0 +1,11 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum RustedComputerError { + #[error("Future failed to resolve")] + FutureFailed, + #[error("Future had a timeout")] + FutureTimeout, +} + +pub type Result = std::result::Result; \ No newline at end of file diff --git a/rustedcomputer-rs/src/functions/mod.rs b/rustedcomputer-rs/src/functions/mod.rs new file mode 100644 index 0000000..4b68c72 --- /dev/null +++ b/rustedcomputer-rs/src/functions/mod.rs @@ -0,0 +1 @@ +pub mod redstone; \ No newline at end of file diff --git a/rustedcomputer-rs/src/functions/redstone.rs b/rustedcomputer-rs/src/functions/redstone.rs new file mode 100644 index 0000000..07b2b4d --- /dev/null +++ b/rustedcomputer-rs/src/functions/redstone.rs @@ -0,0 +1,34 @@ +use crate::async_runtime::RustedComputerFuture; +use crate::error::Result; +use crate::side::Side; + +pub async fn set_output(side: Side, power: u8) -> Result<()> { + let side = side.to_string(); + let side_length = side.len() as i32; + let side_offset = side.as_ptr() as i32; + + let caller = || unsafe { ext_set_output(side_length, side_offset, power as i32) }.into(); + let converter = |_| (); + + RustedComputerFuture::new(caller, converter).await +} + +pub async fn get_input(side: Side) -> Result { + let side = side.to_string(); + let side_length = side.len() as i32; + let side_offset = side.as_ptr() as i32; + + let caller = || unsafe { ext_get_input(side_length, side_offset) }.into(); + let converter = |value: String| value.parse().unwrap(); + + RustedComputerFuture::new(caller, converter).await +} + +#[link(wasm_import_module = "redstone")] +unsafe extern "C" { + #[link_name = "set_output"] + fn ext_set_output(side_length: i32, side_offset: i32, power: i32) -> i32; + + #[link_name = "get_input"] + fn ext_get_input(side_length: i32, side_offset: i32) -> i32; +} diff --git a/rustedcomputer-rs/src/lib.rs b/rustedcomputer-rs/src/lib.rs index b93cf3f..dbc995c 100644 --- a/rustedcomputer-rs/src/lib.rs +++ b/rustedcomputer-rs/src/lib.rs @@ -1,14 +1,11 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; +pub mod async_runtime; +pub mod side; +pub mod functions; +pub mod error; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +#[unsafe(no_mangle)] +pub extern "C" fn alloc(length: i32) -> i32 { + let buffer = vec![0u8; length as usize]; + let boxed_slice = buffer.into_boxed_slice(); + Box::into_raw(boxed_slice) as *mut u8 as i32 } diff --git a/rustedcomputer-rs/src/side.rs b/rustedcomputer-rs/src/side.rs new file mode 100644 index 0000000..66761b2 --- /dev/null +++ b/rustedcomputer-rs/src/side.rs @@ -0,0 +1,25 @@ +use std::fmt::{Display, Formatter, write}; + +pub enum Side { + Top, + Bottom, + Left, + Right, + Front, + Back, +} + +impl Display for Side { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let out = match self { + Side::Top => "top", + Side::Bottom => "bottom", + Side::Left => "left", + Side::Right => "right", + Side::Front => "front", + Side::Back => "back", + }; + + write!(f, "{out}") + } +}