From 88fad02b9634c5b0cbba8f9b9d6b8af482c2f5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linnea=20Gr=C3=A4f?= Date: Sun, 21 Jul 2024 04:42:02 +0200 Subject: [PATCH] Add wasm --- build.gradle | 91 +++++++++--------- .../ajarc/computer/ComputerBlockEntity.java | 11 ++- src/main/java/dev/exhq/ajarc/vm/WasmVm.java | 65 +++++++++++++ src/main/resources/assets/ajarc/test.wasm | Bin 0 -> 77 bytes test.wasm | Bin 0 -> 77 bytes test.wat | 14 +++ 6 files changed, 136 insertions(+), 45 deletions(-) create mode 100644 src/main/java/dev/exhq/ajarc/vm/WasmVm.java create mode 100644 src/main/resources/assets/ajarc/test.wasm create mode 100644 test.wasm create mode 100644 test.wat diff --git a/build.gradle b/build.gradle index 858e75d..ed88d78 100644 --- a/build.gradle +++ b/build.gradle @@ -32,8 +32,50 @@ java.toolchain.languageVersion = JavaLanguageVersion.of(21) //minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg') //minecraft.accessTransformers.entry public net.minecraft.client.Minecraft textureManager # textureManager -// Default run configurations. -// These can be tweaked, removed, or duplicated as needed. +// Include resources generated by data generators. +sourceSets.main.resources { srcDir 'src/generated/resources' } + +// Sets up a dependency configuration called 'localRuntime'. +// This configuration should be used instead of 'runtimeOnly' to declare +// a dependency that will be present for runtime testing but that is +// "optional", meaning it will not be pulled by dependents of this mod. +configurations { + runtimeClasspath.extendsFrom localRuntime + implementation.extendsFrom libraries +} + +dependencies { + // Specify the version of Minecraft to use. + // Depending on the plugin applied there are several options. We will assume you applied the userdev plugin as shown above. + // The group for userdev is net.neoforged, the module name is neoforge, and the version is the same as the neoforge version. + // You can however also use the vanilla plugin (net.neoforged.gradle.vanilla) to use a version of Minecraft without the neoforge loader. + // And its provides the option to then use net.minecraft as the group, and one of; client, server or joined as the module name, plus the game version as version. + // For all intends and purposes: You can treat this dependency as if it is a normal library you would use. + implementation "net.neoforged:neoforge:${neo_version}" + libraries 'com.dylibso.chicory:runtime:0.0.12' + // Example optional mod dependency with JEI + // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime + // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" + // compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}" + // We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it + // localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}" + + // Example mod dependency using a mod jar from ./libs with a flat dir repository + // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar + // The group id is ignored when searching -- in this case, it is "blank" + // implementation "blank:coolmod-${mc_version}:${coolmod_version}" + + // Example mod dependency using a file as dependency + // implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar") + + // Example project dependency using a sister or child project: + // implementation project(":myproject") + + // For more info: + // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html + // http://www.gradle.org/docs/current/userguide/dependency_management.html +} + runs { // applies to all the run configs below configureEach { @@ -50,6 +92,9 @@ runs { systemProperty 'forge.logging.console.level', 'debug' modSource project.sourceSets.main + dependencies { + runtime project.configurations.libraries + } } client { @@ -77,49 +122,7 @@ runs { programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() } -} -// Include resources generated by data generators. -sourceSets.main.resources { srcDir 'src/generated/resources' } - -// Sets up a dependency configuration called 'localRuntime'. -// This configuration should be used instead of 'runtimeOnly' to declare -// a dependency that will be present for runtime testing but that is -// "optional", meaning it will not be pulled by dependents of this mod. -configurations { - runtimeClasspath.extendsFrom localRuntime -} - -dependencies { - // Specify the version of Minecraft to use. - // Depending on the plugin applied there are several options. We will assume you applied the userdev plugin as shown above. - // The group for userdev is net.neoforged, the module name is neoforge, and the version is the same as the neoforge version. - // You can however also use the vanilla plugin (net.neoforged.gradle.vanilla) to use a version of Minecraft without the neoforge loader. - // And its provides the option to then use net.minecraft as the group, and one of; client, server or joined as the module name, plus the game version as version. - // For all intends and purposes: You can treat this dependency as if it is a normal library you would use. - implementation "net.neoforged:neoforge:${neo_version}" - implementation 'com.dylibso.chicory:runtime:0.0.12' - // Example optional mod dependency with JEI - // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime - // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" - // compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}" - // We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it - // localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}" - - // Example mod dependency using a mod jar from ./libs with a flat dir repository - // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar - // The group id is ignored when searching -- in this case, it is "blank" - // implementation "blank:coolmod-${mc_version}:${coolmod_version}" - - // Example mod dependency using a file as dependency - // implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar") - - // Example project dependency using a sister or child project: - // implementation project(":myproject") - - // For more info: - // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html - // http://www.gradle.org/docs/current/userguide/dependency_management.html } // This block of code expands all declared replace properties in the specified resource targets. diff --git a/src/main/java/dev/exhq/ajarc/computer/ComputerBlockEntity.java b/src/main/java/dev/exhq/ajarc/computer/ComputerBlockEntity.java index 7d08981..d6528c2 100644 --- a/src/main/java/dev/exhq/ajarc/computer/ComputerBlockEntity.java +++ b/src/main/java/dev/exhq/ajarc/computer/ComputerBlockEntity.java @@ -4,6 +4,7 @@ import com.mojang.serialization.MapCodec; import dev.exhq.ajarc.Ajar; import dev.exhq.ajarc.network.ComputerScreenUpdate; import dev.exhq.ajarc.register.Register; +import dev.exhq.ajarc.vm.WasmVm; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; @@ -33,7 +34,7 @@ public class ComputerBlockEntity extends BlockEntity { private static final MapCodec screenCodec = ComputerTerminal.CODEC .fieldOf("screen") .setPartial(() -> ComputerTerminal.ofSize(20, 30)); - + private WasmVm vm = new WasmVm(this); private List lines = new ArrayList<>(); @Override @@ -69,6 +70,10 @@ public class ComputerBlockEntity extends BlockEntity { return screen; } + public void setTerminal(ComputerTerminal terminal) { + this.screen = terminal; + } + public void executeCommand(String line) { lines.add("$ " + line); if (line.equals("small")) { @@ -77,6 +82,10 @@ public class ComputerBlockEntity extends BlockEntity { } else if (line.equals("big")) { screen = ComputerTerminal.ofSize(20, 30); lines.add("Made big!"); + } else if (line.equals("add")) { + int left = 42, right = 69; + int result = vm.add(left, right); + lines.add(left + " + " + right + " = " + result); } else { lines.add("Made unknown!"); } diff --git a/src/main/java/dev/exhq/ajarc/vm/WasmVm.java b/src/main/java/dev/exhq/ajarc/vm/WasmVm.java new file mode 100644 index 0000000..fb26537 --- /dev/null +++ b/src/main/java/dev/exhq/ajarc/vm/WasmVm.java @@ -0,0 +1,65 @@ +package dev.exhq.ajarc.vm; + +import com.dylibso.chicory.runtime.HostFunction; +import com.dylibso.chicory.runtime.HostImports; +import com.dylibso.chicory.runtime.Instance; +import com.dylibso.chicory.runtime.Module; +import com.dylibso.chicory.wasm.types.Value; +import com.dylibso.chicory.wasm.types.ValueType; +import dev.exhq.ajarc.Ajar; +import dev.exhq.ajarc.computer.ComputerBlockEntity; +import dev.exhq.ajarc.computer.ComputerTerminal; +import net.minecraft.client.Minecraft; + +import java.io.IOException; +import java.util.List; + +public class WasmVm { + + private final Instance instance; + + public WasmVm(ComputerBlockEntity entity) { + try { + var setSize = new HostFunction( + (instance1, args) -> { + int rows = args[0].asInt(); + int cols = args[1].asInt(); + entity.setTerminal(ComputerTerminal.ofSize(rows, cols)); + return new Value[0]; + }, + "env", + "setSize", + List.of(ValueType.I32, ValueType.I32), + List.of()); + var inputStream = Minecraft.getInstance() + .getResourceManager() + .getResourceOrThrow(Ajar.identifier("test.wasm")) + .open(); + var module = Module.builder(inputStream) + .withHostImports(new HostImports( + new HostFunction[]{ + setSize + } + )) + .build(); + this.instance = module.instantiate(); + } catch (IOException e) { + Ajar.LOGGER.error("Could not load wasm module", e); + throw new RuntimeException(e); + } + } + + public int add(int left, int right) { + var export = instance.export("add"); + var timer = System.nanoTime(); + for (int i = 0; i < 10000; i++) { + export.apply(Value.i32(left), Value.i32(right)); + } + System.out.println("Took " + ((System.nanoTime() - timer) / 1_000_000) + "ms to add 1000*2 numbers"); + var result = export.apply(Value.i32(left), Value.i32(right)); + assert result.length == 1; + return result[0].asInt(); + } + + +} diff --git a/src/main/resources/assets/ajarc/test.wasm b/src/main/resources/assets/ajarc/test.wasm new file mode 100644 index 0000000000000000000000000000000000000000..64937ee16d224f49f4ee94f890ddd2de5f7b85ee GIT binary patch literal 77 zcmZQbEY4+QU|?Y6VM<`CuV(-ejP*<+j2y}Nxdo*qsYUF?sU^XgRjCXN%uI}o?Cgxp di76=zj9fyD0t}8^jywVk3JeO2S&m#;+yHef4v_!= literal 0 HcmV?d00001 diff --git a/test.wasm b/test.wasm new file mode 100644 index 0000000000000000000000000000000000000000..64937ee16d224f49f4ee94f890ddd2de5f7b85ee GIT binary patch literal 77 zcmZQbEY4+QU|?Y6VM<`CuV(-ejP*<+j2y}Nxdo*qsYUF?sU^XgRjCXN%uI}o?Cgxp di76=zj9fyD0t}8^jywVk3JeO2S&m#;+yHef4v_!= literal 0 HcmV?d00001 diff --git a/test.wat b/test.wat new file mode 100644 index 0000000..ccd6a5a --- /dev/null +++ b/test.wat @@ -0,0 +1,14 @@ +(module + (import "computer" "setSize" (func $setSize (param $rows i32) (param $cols i32))) + (func $add (param $left i32) (param $right i32) (result i32) + i32.const 10 + i32.const 12 + call $setSize + local.get $left + local.get $right + i32.add + i32.const 10 + i32.add + ) + (export "add" (func $add)) +) \ No newline at end of file