diff --git a/WaaS.Unity/.gitignore b/WaaS.Unity/.gitignore index 1f042c5..e3884aa 100644 --- a/WaaS.Unity/.gitignore +++ b/WaaS.Unity/.gitignore @@ -71,4 +71,5 @@ crashlytics-build.properties /[Aa]ssets/[Ss]treamingAssets/aa.meta /[Aa]ssets/[Ss]treamingAssets/aa/* -/.idea \ No newline at end of file +/.idea +/target \ No newline at end of file diff --git a/WaaS.Unity/Assets/Component Default.rustimporterpreset b/WaaS.Unity/Assets/Component Default.rustimporterpreset new file mode 100644 index 0000000..e69de29 diff --git a/WaaS.Unity/Assets/Component Default.rustimporterpreset.meta b/WaaS.Unity/Assets/Component Default.rustimporterpreset.meta new file mode 100644 index 0000000..7bef770 --- /dev/null +++ b/WaaS.Unity/Assets/Component Default.rustimporterpreset.meta @@ -0,0 +1,27 @@ +fileFormatVersion: 2 +guid: a4b228cdd360ad14a949d7d64fd90961 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: f90bfd9334e241909e8cfc9612ef7ff7, type: 3} + settings: + dependencies: + - rid: 8955242026984538113 + componentize: 1 + componentizationSettings: + witDirectory: wit + world: my-game:my-sequencer/sequence + references: + version: 2 + RefIds: + - rid: 8955242026984538113 + type: {class: RustImporterSettings/PackageRegistryDependency, ns: WaaS.Unity.Editor.Rust, asm: WaaS.Unity.Editor} + data: + name: wit-bindgen + versionRequirement: [] + features: [] + registry: diff --git a/WaaS.Unity/Assets/RunSequenceTest.cs b/WaaS.Unity/Assets/RunSequenceTest.cs new file mode 100644 index 0000000..c36631f --- /dev/null +++ b/WaaS.Unity/Assets/RunSequenceTest.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using MyGame.MySequencer; +using UnityEngine; +using WaaS.ComponentModel.Runtime; +using WaaS.Runtime; +using WaaS.Unity; + +public class RunSequenceTest : MonoBehaviour +{ + [SerializeField] private ComponentAsset componentAsset; + + private async void Start() + { + var component = await componentAsset.LoadComponentAsync(); + var instance = component.Instantiate(new Dictionary + { + { "my-game:my-sequencer/env", IEnv.CreateWaaSInstance(new Env()) } + }); + using var context = new ExecutionContext(); + var sequence = new ISequence.Wrapper(instance, context); + await sequence.Play(); // ぼく「こんにちは!」 + } + + private class Env : IEnv + { + public ValueTask ShowMessage(string speaker, string message) + { + Debug.Log($"{speaker}「{message}」"); + return default; + } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Assets/RunSequenceTest.cs.meta b/WaaS.Unity/Assets/RunSequenceTest.cs.meta new file mode 100644 index 0000000..9794ab0 --- /dev/null +++ b/WaaS.Unity/Assets/RunSequenceTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f604d50041ce4bdbb992ada3234b2307 +timeCreated: 1733839786 \ No newline at end of file diff --git a/WaaS.Unity/Assets/WaaS Generated.meta b/WaaS.Unity/Assets/WaaS Generated.meta new file mode 100644 index 0000000..5fec8ce --- /dev/null +++ b/WaaS.Unity/Assets/WaaS Generated.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fd90512c0290d48448472617c2a8b960 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WaaS.Unity/Assets/WaaS Generated/my-game.meta b/WaaS.Unity/Assets/WaaS Generated/my-game.meta new file mode 100644 index 0000000..47142ac --- /dev/null +++ b/WaaS.Unity/Assets/WaaS Generated/my-game.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b3e2cd759f90f1243b478b3b8eefce7b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer.meta b/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer.meta new file mode 100644 index 0000000..cde75f3 --- /dev/null +++ b/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dc4d374d52878b94aa0109ccd80fc830 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Env.g.cs b/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Env.g.cs new file mode 100644 index 0000000..ecebc1d --- /dev/null +++ b/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Env.g.cs @@ -0,0 +1,14 @@ +// +#nullable enable + +namespace MyGame.MySequencer +{ + // interface env + [global::WaaS.ComponentModel.Binding.ComponentInterface(@"env")] + public partial interface IEnv + { + [global::WaaS.ComponentModel.Binding.ComponentApi(@"show-message")] + global::System.Threading.Tasks.ValueTask ShowMessage(string @speaker, string @message); + + } +} diff --git a/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Env.g.cs.meta b/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Env.g.cs.meta new file mode 100644 index 0000000..7075ef7 --- /dev/null +++ b/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Env.g.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a961c7741592d8246841400532e36e64 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Sequence.g.cs b/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Sequence.g.cs new file mode 100644 index 0000000..31556b4 --- /dev/null +++ b/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Sequence.g.cs @@ -0,0 +1,14 @@ +// +#nullable enable + +namespace MyGame.MySequencer +{ + // world sequence + [global::WaaS.ComponentModel.Binding.ComponentInterface(@"sequence")] + public partial interface ISequence + { + [global::WaaS.ComponentModel.Binding.ComponentApi(@"play")] + global::System.Threading.Tasks.ValueTask Play(); + + } +} diff --git a/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Sequence.g.cs.meta b/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Sequence.g.cs.meta new file mode 100644 index 0000000..0911564 --- /dev/null +++ b/WaaS.Unity/Assets/WaaS Generated/my-game/my-sequencer/Sequence.g.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d3a46f32f9dd74c4b89bb48b8ed9955d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WaaS.Unity/Assets/test.rs b/WaaS.Unity/Assets/test.rs new file mode 100644 index 0000000..c21a40d --- /dev/null +++ b/WaaS.Unity/Assets/test.rs @@ -0,0 +1,16 @@ +use crate::my_game::my_sequencer::env::show_message; + +wit_bindgen::generate!({ + path: "../../../../../wit", + world: "my-game:my-sequencer/sequence" +}); + +struct Sequence; + +impl Guest for Sequence { + fn play() { + show_message("ぼく", "こんにちは!"); + } +} + +export!(Sequence); \ No newline at end of file diff --git a/WaaS.Unity/Assets/test.rs.meta b/WaaS.Unity/Assets/test.rs.meta new file mode 100644 index 0000000..8ae6930 --- /dev/null +++ b/WaaS.Unity/Assets/test.rs.meta @@ -0,0 +1,21 @@ +fileFormatVersion: 2 +guid: b3c3b8206d3c58c4db6b2bfe2e6b64d1 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 355dc2c982e1aad48a051b54164d5b15, type: 3} + crateRoot: 1 + preset: {fileID: 4218292774016059762, guid: a4b228cdd360ad14a949d7d64fd90961, type: 3} + settings: + dependencies: [] + componentize: 0 + componentizationSettings: + witDirectory: wit + world: + references: + version: 2 + RefIds: [] diff --git a/WaaS.Unity/Cargo.lock b/WaaS.Unity/Cargo.lock new file mode 100644 index 0000000..2707915 --- /dev/null +++ b/WaaS.Unity/Cargo.lock @@ -0,0 +1,468 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "anyhow" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cargo_component_example" +version = "0.0.0" +dependencies = [ + "wee_alloc", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.167" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "spdx" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae30cc7bfe3656d60ee99bf6836f472b0c53dddcbf335e253329abb16e535a2" +dependencies = [ + "smallvec", +] + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "waas_04e4d7aa903d8ab79fc3709e39575ff0" +version = "0.0.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "waas_5e8481e1b0dcd8c30f11999e44cb923a" +version = "0.0.0" +dependencies = [ + "wee_alloc", +] + +[[package]] +name = "waas_6f948bb03c4c4f92db2ef9b821e45602" +version = "0.0.0" +dependencies = [ + "wee_alloc", +] + +[[package]] +name = "waas_71226cfc156f30f4a0f64b971dde1bde" +version = "0.0.0" + +[[package]] +name = "waas_bb474e09def1e135467e52d90f36ebef" +version = "0.0.0" + +[[package]] +name = "wasm-encoder" +version = "0.220.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf48234b389415b226a4daef6562933d38c7b28a8b8f64c5c4130dad1561ab7" +dependencies = [ + "leb128", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.220.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3e5f5920c5abfc45573c89b07b38efdaae1515ef86f83dad12d60e50ecd62b" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.220.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e246c2772ce3ebc83f89a2d4487ac5794cad6c309b2071818a88c7db7c36d87b" +dependencies = [ + "ahash", + "bitflags", + "hashbrown 0.14.5", + "indexmap", + "semver", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "wit-bindgen" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a2b3e15cd6068f233926e7d8c7c588b2ec4fb7cc7bf3824115e7c7e2a8485a3" +dependencies = [ + "wit-bindgen-rt", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b632a5a0fa2409489bd49c9e6d99fcc61bb3d4ce9d1907d44662e75a28c71172" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7947d0131c7c9da3f01dfde0ab8bd4c4cf3c5bd49b6dba0ae640f1fa752572ea" +dependencies = [ + "bitflags", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4329de4186ee30e2ef30a0533f9b3c123c019a237a7c82d692807bf1b3ee2697" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177fb7ee1484d113b4792cc480b1ba57664bbc951b42a4beebe573502135b1fc" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.220.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ccedf54cc65f287da268d64d2bf4f7530d2cfb2296ffbe3ad5f65567e4cf53" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.220.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7117ce3adc0b4354b46dc1cf3190b00b333e65243d244c613ffcc58bdec84d" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/WaaS.Unity/Cargo.toml b/WaaS.Unity/Cargo.toml new file mode 100644 index 0000000..26bb697 --- /dev/null +++ b/WaaS.Unity/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["Library/com.ruccho.waas/rust/crates/*"] # Used by WaaS +resolver = "2" # Used by WaaS \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Models/Component.cs b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Models/Component.cs index 45234f1..4a32022 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Models/Component.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Models/Component.cs @@ -13,7 +13,7 @@ namespace WaaS.ComponentModel.Models /// /// Represents a WebAssembly component. /// - public class Component : IUnresolved + public class UnresolvedComponent : IUnresolved { private readonly List customSections = new(); private readonly List> exports = new(); @@ -21,7 +21,7 @@ public class Component : IUnresolved private readonly List> imports = new(); private readonly List> instantiations = new(); - internal Component(ref ModuleReader reader, long? size = null, IIndexSpace? parentIndexSpace = null) + internal UnresolvedComponent(ref ModuleReader reader, long? size = null, IIndexSpace? parentIndexSpace = null) { long rest = 0; if (size.HasValue) @@ -77,7 +77,7 @@ internal Component(ref ModuleReader reader, long? size = null, IIndexSpace? pare } case SectionId.Component: { - indexSpace.Add(new Component(ref reader, sectionSize, indexSpace)); + indexSpace.Add(new UnresolvedComponent(ref reader, sectionSize, indexSpace)); break; } case SectionId.Instance: @@ -134,6 +134,10 @@ internal Component(ref ModuleReader reader, long? size = null, IIndexSpace? pare } } + public IEnumerable CustomSections => customSections; + public IEnumerable> Exports => exports; + public IEnumerable> Imports => imports; + public IComponent ResolveFirstTime(IInstantiationContext context) { return new CapturedComponent(this, context); @@ -147,7 +151,7 @@ public IComponent ResolveFirstTime(IInstantiationContext context) public static IComponent Create(ReadOnlySpan buffer) { var reader = new ModuleReader(buffer); - return new CapturedComponent(new Component(ref reader), null); + return new CapturedComponent(new UnresolvedComponent(ref reader), null); } /// @@ -158,20 +162,21 @@ public static IComponent Create(ReadOnlySpan buffer) public static IComponent Create(ReadOnlySequence buffer) { var reader = new ModuleReader(buffer); - return new CapturedComponent(new Component(ref reader), null); + return new CapturedComponent(new UnresolvedComponent(ref reader), null); } private class CapturedComponent : IComponent { private readonly IInstantiationContext? outerContext; - private readonly Component source; - public CapturedComponent(Component source, IInstantiationContext? outerContext) + public CapturedComponent(UnresolvedComponent source, IInstantiationContext? outerContext) { - this.source = source; + this.Source = source; this.outerContext = outerContext; } + public UnresolvedComponent Source { get; } + public IInstance Instantiate(IReadOnlyDictionary arguments) { Dictionary resolvedExports = new(); @@ -180,7 +185,7 @@ public IInstance Instantiate(IReadOnlyDictionary argu var newContext = new InstantiationContext(instance, outerContext, arguments); List>? missingImports = null; - foreach (var import in source.imports) + foreach (var import in Source.imports) { arguments.TryGetValue(import.Name.Name, out var argument); if (!import.Descriptor.ValidateArgument(newContext, argument)) @@ -194,13 +199,13 @@ public IInstance Instantiate(IReadOnlyDictionary argu throw new LinkException( $"The import is missing or invalid: \n{string.Join("\n", missingImports.Select(i => i.Name.Name))}"); - foreach (var export in source.exports) + foreach (var export in Source.exports) resolvedExports.Add(export.Name.Name, newContext.Resolve(export.Target)); // instantiations need to be resolved (unexposed instances may initialize other instance's state) - for (var i = 0; i < source.instantiations.Count; i++) + for (var i = 0; i < Source.instantiations.Count; i++) { - var instantiation = source.instantiations[i]; + var instantiation = Source.instantiations[i]; newContext.Resolve(instantiation); } @@ -287,7 +292,7 @@ public bool Validate(IInstantiationContext context, ICoreModuleType coreModuleTy if (importSection != null) foreach (var import in importSection.Imports.Span) { - var desc = import.Description; + var desc = import.Descriptor; if (desc.Kind == ImportKind.Type) if (index++ == descModuleFuncIndex) { @@ -315,11 +320,11 @@ public bool Validate(IInstantiationContext context, ICoreModuleType coreModuleTy if (importSection != null) foreach (var import in importSection.Imports.Span) { - var desc = import.Description; + var desc = import.Descriptor; if (desc.Kind == ImportKind.Table) if (index++ == providedDesc.TableIndex) { - providedTableType = import.Description.TableType!.Value; + providedTableType = import.Descriptor.TableType!.Value; goto FOUND; } } @@ -346,11 +351,11 @@ public bool Validate(IInstantiationContext context, ICoreModuleType coreModuleTy if (importSection != null) foreach (var import in importSection.Imports.Span) { - var desc = import.Description; + var desc = import.Descriptor; if (desc.Kind == ImportKind.Memory) if (index++ == providedDesc.MemoryIndex) { - providedType = import.Description.MemoryType!.Value; + providedType = import.Descriptor.MemoryType!.Value; goto FOUND; } } @@ -375,11 +380,11 @@ public bool Validate(IInstantiationContext context, ICoreModuleType coreModuleTy if (importSection != null) foreach (var import in importSection.Imports.Span) { - var desc = import.Description; + var desc = import.Descriptor; if (desc.Kind == ImportKind.Global) if (index++ == providedDesc.GlobalIndex) { - providedType = import.Description.GlobalType!.Value; + providedType = import.Descriptor.GlobalType!.Value; goto FOUND; } } @@ -409,7 +414,7 @@ public bool Validate(IInstantiationContext context, ICoreModuleType coreModuleTy if (coreImportDecl.ModuleName == import.ModuleName && coreImportDecl.Name == import.Name && - coreImportDecl.Kind == import.Description.Kind) + coreImportDecl.Kind == import.Descriptor.Kind) { importDecl = coreImportDecl; goto FOUND; @@ -421,7 +426,7 @@ public bool Validate(IInstantiationContext context, ICoreModuleType coreModuleTy FOUND: ; } - var requestedImport = import.Description; + var requestedImport = import.Descriptor; switch (requestedImport.Kind) { case ImportKind.Type: diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Models/CoreTypes.cs b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Models/CoreTypes.cs index 6ab0351..8c132d8 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Models/CoreTypes.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Models/CoreTypes.cs @@ -131,11 +131,11 @@ public bool TryRead(ref ModuleReader reader, IIndexSpace indexSpace, out CoreImp { var import = new Import(ref reader); IUnresolved? type = null; - switch (import.Description.Kind) + switch (import.Descriptor.Kind) { case ImportKind.Type: { - type = indexSpace.Get(import.Description.TypeIndex!.Value); + type = indexSpace.Get(import.Descriptor.TypeIndex!.Value); break; } case ImportKind.Table: @@ -146,7 +146,7 @@ public bool TryRead(ref ModuleReader reader, IIndexSpace indexSpace, out CoreImp throw new ArgumentOutOfRangeException(); } - result = new CoreImportDeclaration(import.ModuleName, import.Name, import.Description, type); + result = new CoreImportDeclaration(import.ModuleName, import.Name, import.Descriptor, type); return true; } } diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Runtime/Interfaces.cs b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Runtime/Interfaces.cs index 8641c12..2f93273 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Runtime/Interfaces.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/ComponentModel/Runtime/Interfaces.cs @@ -58,6 +58,7 @@ public interface IType : ISortedExportable public interface IComponent : ISortedExportable { + UnresolvedComponent Source { get; } IInstance Instantiate(IReadOnlyDictionary arguments); } diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Models/Sections/ImportSection.cs b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Models/Sections/ImportSection.cs index ce26793..0a2d965 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Models/Sections/ImportSection.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Models/Sections/ImportSection.cs @@ -27,18 +27,18 @@ internal ImportSection(ref ModuleReader reader) { public string ModuleName { get; } public string Name { get; } - public ImportDescriptor Description { get; } + public ImportDescriptor Descriptor { get; } internal Import(ref ModuleReader reader) { ModuleName = reader.ReadUtf8String(); Name = reader.ReadUtf8String(); - Description = new ImportDescriptor(ref reader); + Descriptor = new ImportDescriptor(ref reader); } public bool Equals(Import other) { - return ModuleName == other.ModuleName && Name == other.Name && Description.Equals(other.Description); + return ModuleName == other.ModuleName && Name == other.Name && Descriptor.Equals(other.Descriptor); } public override bool Equals(object obj) @@ -48,7 +48,7 @@ public override bool Equals(object obj) public override int GetHashCode() { - return HashCode.Combine(ModuleName, Name, Description); + return HashCode.Combine(ModuleName, Name, Descriptor); } public static bool operator ==(Import left, Import right) diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Models/Validation/ValidationContext.cs b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Models/Validation/ValidationContext.cs index 22b703b..9fa3e4d 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Models/Validation/ValidationContext.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Models/Validation/ValidationContext.cs @@ -26,7 +26,7 @@ public FunctionType GetFunctionType(uint runtimeFunctionIndex) var i = 0; foreach (var import in imports) { - var t = import.Description.TypeIndex; + var t = import.Descriptor.TypeIndex; if (!t.HasValue) continue; if (i == runtimeFunctionIndex) return Module.TypeSection.FuncTypes.Span[(int)t.Value]; @@ -68,7 +68,7 @@ public GlobalType GetGlobalType(uint index) var i = 0; foreach (var import in imports) { - var t = import.Description.GlobalType; + var t = import.Descriptor.GlobalType; if (!t.HasValue) continue; if (i == index) return t.Value; @@ -90,7 +90,7 @@ public TableType GetTableType(uint index) var i = 0; foreach (var import in imports) { - var t = import.Description.TableType; + var t = import.Descriptor.TableType; if (!t.HasValue) continue; if (i == index) return t.Value; @@ -112,7 +112,7 @@ public MemoryType GetMemoryType(uint index) var i = 0; foreach (var import in imports) { - var t = import.Description.MemoryType; + var t = import.Descriptor.MemoryType; if (!t.HasValue) continue; if (i == index) return t.Value; diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/FunctionInstance.cs b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/FunctionInstance.cs index 856c55e..2f8d22f 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/FunctionInstance.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/FunctionInstance.cs @@ -17,7 +17,7 @@ public FunctionInstance(Instance instance, ImportSection importSection, IImports var numFunctions = moduleFunctions.Length; foreach (var import in imports) { - var t = import.Description.TypeIndex; + var t = import.Descriptor.TypeIndex; if (!t.HasValue) continue; numFunctions++; @@ -27,7 +27,7 @@ public FunctionInstance(Instance instance, ImportSection importSection, IImports var cursor = 0; foreach (var import in imports) { - var t = import.Description.TypeIndex; + var t = import.Descriptor.TypeIndex; if (!t.HasValue) continue; if (!importObject.TryGetImportable(import.ModuleName, import.Name, diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/GlobalInstance.cs b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/GlobalInstance.cs index ef49fc5..d53e361 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/GlobalInstance.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/GlobalInstance.cs @@ -14,7 +14,7 @@ internal GlobalInstance(ImportSection importSection, GlobalSection globalSection var imports = importSection != null ? importSection.Imports.Span : Span.Empty; var numGlobals = 0; foreach (var import in imports) - if (import.Description.GlobalType.HasValue) + if (import.Descriptor.GlobalType.HasValue) numGlobals++; numGlobals += globalSection?.Globals.Length ?? 0; @@ -24,7 +24,7 @@ internal GlobalInstance(ImportSection importSection, GlobalSection globalSection var cursor = 0; foreach (var import in imports) { - var globalType = import.Description.GlobalType; + var globalType = import.Descriptor.GlobalType; if (!globalType.HasValue) continue; if (!importObject.TryGetImportable(import.ModuleName, import.Name, out Global global) || diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/MemoryInstance.cs b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/MemoryInstance.cs index a6696b7..1bb840d 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/MemoryInstance.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/MemoryInstance.cs @@ -15,7 +15,7 @@ public MemoryInstance(MemorySection memorySection, ImportSection importSection, var numMemories = internalMemoryTypes.Length; var importItems = importSection != null ? importSection.Imports.Span : Span.Empty; foreach (var import in importItems) - if (import.Description.MemoryType.HasValue) + if (import.Descriptor.MemoryType.HasValue) numMemories++; if (numMemories > 1) throw new InvalidModuleException("multiple memories"); @@ -25,7 +25,7 @@ public MemoryInstance(MemorySection memorySection, ImportSection importSection, foreach (var import in importItems) { - var t = import.Description.MemoryType; + var t = import.Descriptor.MemoryType; if (t.HasValue) { diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/TableInstance.cs b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/TableInstance.cs index e01d576..8aff59c 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/TableInstance.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Core/Scripts/Runtime/TableInstance.cs @@ -15,7 +15,7 @@ public TableInstance(GlobalInstance globals, FunctionInstance functions, var numTables = tableSection?.TableTypes.Length ?? 0; var imports = importSection != null ? importSection.Imports.Span : Span.Empty; foreach (var import in imports) - if (import.Description.TableType != null) + if (import.Descriptor.TableType != null) numTables++; if (numTables > 1) throw new InvalidModuleException("multiple tables"); @@ -25,7 +25,7 @@ public TableInstance(GlobalInstance globals, FunctionInstance functions, var cursor = 0; foreach (var import in imports) { - var tableType = import.Description.TableType; + var tableType = import.Descriptor.TableType; if (tableType.HasValue) { var t = tableType.Value; diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Custom Resources.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Custom Resources.meta new file mode 100644 index 0000000..4399bac --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Custom Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 201da80320703d2418dbf8c3d1d9d2bc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Custom Resources/ModuleAsset.png b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Custom Resources/ModuleAsset.png new file mode 100644 index 0000000..b1e4ba3 Binary files /dev/null and b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Custom Resources/ModuleAsset.png differ diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Custom Resources/ModuleAsset.png.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Custom Resources/ModuleAsset.png.meta new file mode 100644 index 0000000..c20af01 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Custom Resources/ModuleAsset.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 470c37360287f0d4697fc18fdd47c97a +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 256 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Plugins.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Plugins.meta new file mode 100644 index 0000000..13b0180 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e17cbf8d223f0e44b889ae1409722d99 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Plugins/waas_unity_native_editor.dll b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Plugins/waas_unity_native_editor.dll new file mode 100644 index 0000000..d296cf9 Binary files /dev/null and b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Plugins/waas_unity_native_editor.dll differ diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Plugins/waas_unity_native_editor.dll.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Plugins/waas_unity_native_editor.dll.meta new file mode 100644 index 0000000..38d1e0a --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Plugins/waas_unity_native_editor.dll.meta @@ -0,0 +1,63 @@ +fileFormatVersion: 2 +guid: c35575616e3759c49b6b2eb64df94e8c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: Windows + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentAssetEditor.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentAssetEditor.cs new file mode 100644 index 0000000..2ff6c67 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentAssetEditor.cs @@ -0,0 +1,63 @@ +using UnityEditor; +using UnityEngine; +using WaaS.ComponentModel.Models; +using WaaS.ComponentModel.Runtime; + +namespace WaaS.Unity.Editor +{ + [CustomEditor(typeof(ComponentAsset))] + public class ComponentAssetEditor : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + + var componentAsset = (ComponentAsset)target; + GUILayout.Label($"{componentAsset.Size} bytes"); + var componentSource = componentAsset.LoadComponent().Source; + + GUILayout.Label("Imports", EditorStyles.boldLabel); + + foreach (var import in componentSource.Imports) + EditorGUILayout.LabelField(import.Name.Name, + import.Descriptor switch + { + IExportableDescriptor => "Function", + IExportableDescriptor => "Value", + IExportableDescriptor => "Type", + IExportableDescriptor => "Component", + IExportableDescriptor => "Instance", + IExportableDescriptor => "Core Module", + _ => $"Unknown ({import.Descriptor?.GetType()})" + }); + + GUILayout.Label("Exports", EditorStyles.boldLabel); + + foreach (var export in componentSource.Exports) + EditorGUILayout.LabelField(export.Name.Name, GetSort(export.Target)); + } + + private string GetSort(IUnresolved sorted) + { + switch (sorted) + { + case IUnresolved: + return "Component"; + case IUnresolved: + return "Module"; + case IUnresolved: + return "Core Type"; + case IUnresolved: + return "Function"; + case IUnresolved: + return "Instance"; + case IUnresolved: + return "Type"; + case IUnresolved: + return "Value"; + default: + return $"Unknown ({sorted.GetType()})"; + } + } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentAssetEditor.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentAssetEditor.cs.meta new file mode 100644 index 0000000..0b37653 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentAssetEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a012c2a63b9e4b7685223e5662446670 +timeCreated: 1733800677 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentizationSettings.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentizationSettings.cs new file mode 100644 index 0000000..78dbb50 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentizationSettings.cs @@ -0,0 +1,15 @@ +using System; +using UnityEngine; + +namespace WaaS.Unity.Editor +{ + [Serializable] + public class ComponentizationSettings + { + [SerializeField] private string witDirectory = "wit"; + [SerializeField] private string world; + + public string WitDirectory => witDirectory; + public string World => world; + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentizationSettings.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentizationSettings.cs.meta new file mode 100644 index 0000000..7c763e7 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ComponentizationSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: adfaee0e2fea4986af1001c08c856a3f +timeCreated: 1733790366 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ModuleAssetEditor.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ModuleAssetEditor.cs index bc11d63..354753b 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ModuleAssetEditor.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/ModuleAssetEditor.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections; -using System.Collections.Generic; using UnityEditor; using UnityEngine; -using WaaS.Unity; namespace WaaS.Unity.Editor { @@ -14,18 +10,24 @@ public override void OnInspectorGUI() { base.OnInspectorGUI(); - var moduleAsset = (ModuleAsset) target; + var moduleAsset = (ModuleAsset)target; + GUILayout.Label($"{moduleAsset.Size} bytes"); var module = moduleAsset.LoadModule(); var imports = module.ImportSection?.Imports; + GUILayout.Label("Imports", EditorStyles.boldLabel); + if (imports != null) - { foreach (var import in imports.Value.Span) - { - EditorGUILayout.LabelField(import.ModuleName, $"{import.Name} ({import.Description.Kind})"); - } - } + EditorGUILayout.LabelField(import.ModuleName, $"{import.Name} ({import.Descriptor.Kind})"); + + var exports = module.ExportSection?.Exports; + + GUILayout.Label("Exports", EditorStyles.boldLabel); + if (exports != null) + foreach (var export in exports.Value.Span) + EditorGUILayout.LabelField(export.Name, $"({export.Descriptor.Kind})"); } } -} +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/NativeMethods.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/NativeMethods.cs new file mode 100644 index 0000000..4c2a86c --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/NativeMethods.cs @@ -0,0 +1,69 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WaaS.Unity.Editor +{ + internal unsafe class NativeMethods + { + private const string DllName = "waas_unity_native_editor"; + + private static nint? onSuccess; + private static nint? onError; + + [DllImport(DllName, CharSet = CharSet.Ansi)] + private static extern void componentize( + byte* source, + nint source_len, + string wit_path, + string world, + StringEncoding encoding, + void* user_data, + nint on_success, + nint on_error); + + public static byte[] Componentize( + ReadOnlySpan source, + string witPath, + string world, + StringEncoding encoding) + { + object result = null; + + static void OnSuccess(nint userData, nint ptr, nint len) + { + var result = new byte[len]; + Marshal.Copy(ptr, result, 0, (int)len); + Unsafe.AsRef((void*)userData) = result; + } + + static void OnError(nint userData, nint message) + { + Unsafe.AsRef((void*)userData) = Marshal.PtrToStringAnsi(message); + } + + var onSuccess = NativeMethods.onSuccess ??= + Marshal.GetFunctionPointerForDelegate((Action)OnSuccess); + var onError = NativeMethods.onError ??= Marshal.GetFunctionPointerForDelegate((Action)OnError); + + var resultPtr = Unsafe.AsPointer(ref result); + fixed (byte* sourcePtr = source) + { + componentize(sourcePtr, source.Length, witPath, world, encoding, resultPtr, onSuccess, onError); + } + + if (result is byte[] bytes) return bytes; + + if (result is string message) throw new Exception(message); + + throw new Exception(); + } + } + + internal enum StringEncoding : uint + { + UTF8, + UTF16, + CompactUTF16 + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/NativeMethods.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/NativeMethods.cs.meta new file mode 100644 index 0000000..8bc857e --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/NativeMethods.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 218ec751ceef4d0eaaaebbb5408a2b7e +timeCreated: 1733795057 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust.meta new file mode 100644 index 0000000..c639b33 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c26a063627154fed80e477ece3e4fa49 +timeCreated: 1733626158 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustImporterSettings.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustImporterSettings.cs new file mode 100644 index 0000000..2b254d0 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustImporterSettings.cs @@ -0,0 +1,12 @@ +#nullable enable + +using System.Collections.Generic; + +namespace WaaS.Unity.Editor.Rust +{ + public interface IRustImporterSettings + { + IEnumerable Dependencies { get; } + ComponentizationSettings? ComponentizationSettings { get; } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustImporterSettings.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustImporterSettings.cs.meta new file mode 100644 index 0000000..f9954c6 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustImporterSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 90a2d4c4cc874fe59346576cffde5b9c +timeCreated: 1733626136 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageDependency.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageDependency.cs new file mode 100644 index 0000000..ab6926a --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageDependency.cs @@ -0,0 +1,12 @@ +#nullable enable +using System.Collections.Generic; + +namespace WaaS.Unity.Editor.Rust +{ + public interface IRustPackageDependency + { + string Name { get; } + IEnumerable? VersionRequirements { get; } + string[]? Features { get; } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageDependency.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageDependency.cs.meta new file mode 100644 index 0000000..997058d --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageDependency.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4aad17b7754e4b0a8bf042e6d1128588 +timeCreated: 1733627344 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageGitDependency.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageGitDependency.cs new file mode 100644 index 0000000..134c105 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageGitDependency.cs @@ -0,0 +1,11 @@ +#nullable enable +namespace WaaS.Unity.Editor.Rust +{ + public interface IRustPackageGitDependency : IRustPackageDependency + { + string GitUrl { get; } + string? Branch { get; } + string? Tag { get; } + string? Rev { get; } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageGitDependency.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageGitDependency.cs.meta new file mode 100644 index 0000000..f7606cb --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageGitDependency.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d21f1620e232433189a3f801936ed44d +timeCreated: 1733627351 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageRegistryDependency.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageRegistryDependency.cs new file mode 100644 index 0000000..6beb44e --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageRegistryDependency.cs @@ -0,0 +1,11 @@ +#nullable enable +using System.Collections.Generic; + +namespace WaaS.Unity.Editor.Rust +{ + public interface IRustPackageRegistryDependency : IRustPackageDependency + { + new IEnumerable VersionRequirements { get; } + string? Registry { get; } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageRegistryDependency.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageRegistryDependency.cs.meta new file mode 100644 index 0000000..d0437b7 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IRustPackageRegistryDependency.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8eca3e6c5ce944fbaaa299082f110338 +timeCreated: 1733627348 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IVersionRequirements.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IVersionRequirements.cs new file mode 100644 index 0000000..51e391f --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IVersionRequirements.cs @@ -0,0 +1,9 @@ +#nullable enable +namespace WaaS.Unity.Editor.Rust +{ + public interface IVersionRequirement + { + VersionRequirementKind Kind { get; } + string Version { get; } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IVersionRequirements.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IVersionRequirements.cs.meta new file mode 100644 index 0000000..5efa704 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/IVersionRequirements.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 449dcbc4d8914a0d91d703105db03100 +timeCreated: 1733627354 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporter.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporter.cs new file mode 100644 index 0000000..69045be --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporter.cs @@ -0,0 +1,330 @@ +#if !WAAS_DISABLE_RUST_IMPORTER +#nullable enable +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.AssetImporters; +using UnityEngine; + +namespace WaaS.Unity.Editor.Rust +{ + [ScriptedImporter(1, "rs", 100)] + public class RustImporter : ScriptedImporter + { + [SerializeField] private bool crateRoot = true; + [SerializeField] private LazyLoadReference preset; + [SerializeField] private RustImporterSettings? settings; + + public override void OnImportAsset(AssetImportContext ctx) + { + if (!crateRoot) + { + ctx.SetMainObject(null); + return; + } + + EnsureWorkspaceCreated(); + + var assetPath = ctx.assetPath; + var hash = Hash128.Compute(assetPath); + var cratePath = $"Library/com.ruccho.waas/rust/crates/waas_{hash}"; + Directory.CreateDirectory(cratePath); + var cargoTomlPath = Path.Combine(cratePath, "Cargo.toml"); + + var cargoTomlBuilder = new StringBuilder(); + + cargoTomlBuilder.AppendLine( + $"[package]\nname = \"waas_{hash}\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\npath = \"../../../../../{assetPath}\""); + cargoTomlBuilder.AppendLine("[dependencies]"); + + RustImporterSettings settings; + if (preset.isSet && + AssetDatabase.TryGetGUIDAndLocalFileIdentifier(preset, out var presetGuid, out var presetLocalId)) + { + ctx.DependsOnArtifact(new GUID(presetGuid)); + AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(presetGuid)); + settings = preset.asset.settings; + } + else + { + settings = this.settings ?? new RustImporterSettings(); + } + + foreach (var dependency in settings.Dependencies) + { + cargoTomlBuilder.Append(dependency.Name); + cargoTomlBuilder.Append(" = {"); + if (dependency.VersionRequirements is { } versionRequirements) + { + var any = false; + cargoTomlBuilder.Append(" version = \""); + foreach (var versionRequirement in versionRequirements) + { + if (any) cargoTomlBuilder.Append(", "); + + any = true; + _ = versionRequirement.Kind switch + { + VersionRequirementKind.Caret => cargoTomlBuilder.Append('^'), + VersionRequirementKind.Tilde => cargoTomlBuilder.Append('~'), + VersionRequirementKind.GreaterThan => cargoTomlBuilder.Append('>'), + VersionRequirementKind.GreaterThanOrEqual => cargoTomlBuilder.Append(">="), + VersionRequirementKind.LessThan => cargoTomlBuilder.Append('<'), + VersionRequirementKind.LessThanOrEqual => cargoTomlBuilder.Append("<="), + VersionRequirementKind.Equal => cargoTomlBuilder.Append('='), + _ => throw new ArgumentOutOfRangeException() + }; + + cargoTomlBuilder.Append(versionRequirement.Version); + } + + if (!any) cargoTomlBuilder.Append("*"); + + cargoTomlBuilder.Append("\""); + } + + switch (dependency) + { + case IRustPackageRegistryDependency regDep: + { + if (regDep.Registry is { } registry) + { + cargoTomlBuilder.Append(", registry = \""); + cargoTomlBuilder.Append(registry); + cargoTomlBuilder.Append("\""); + } + + break; + } + case IRustPackageGitDependency gitDep: + { + cargoTomlBuilder.Append(" git = \""); + cargoTomlBuilder.Append(gitDep.GitUrl); + cargoTomlBuilder.Append("\""); + if (gitDep.Branch is { } branch) + { + cargoTomlBuilder.Append(", branch = \""); + cargoTomlBuilder.Append(branch); + cargoTomlBuilder.Append("\""); + } + + if (gitDep.Tag is { } tag) + { + cargoTomlBuilder.Append(", tag = \""); + cargoTomlBuilder.Append(tag); + cargoTomlBuilder.Append("\""); + } + + if (gitDep.Rev is { } rev) + { + cargoTomlBuilder.Append(", rev = \""); + cargoTomlBuilder.Append(rev); + cargoTomlBuilder.Append("\""); + } + + break; + } + default: + throw new ArgumentOutOfRangeException(nameof(dependency)); + } + + cargoTomlBuilder.AppendLine(" }"); + } + + File.WriteAllText(cargoTomlPath, + cargoTomlBuilder.ToString(), + Encoding.UTF8); + + var args = + $@"build -p ""waas_{hash}"" --target wasm32-unknown-unknown --release --target-dir Library/com.ruccho.waas/rust/target --config ""build.dep-info-basedir=\"".\"""""; + var psi = new ProcessStartInfo( +#if UNITY_EDITOR_WIN + "cargo", args +#else // in macOS, we need to run bash gracefully in order to set the PATH correctly + "/bin/bash", $"-cl 'cargo {args}'" +#endif + ) + { + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true + }; + + var process = Process.Start(psi); + + var messageQueue = new ConcurrentQueue(); + + void LogStream(StreamReader reader, StringBuilder builder) + { + Task.Run(async () => + { + while (!reader.EndOfStream) + { + var line = await reader.ReadLineAsync(); + builder.AppendLine(line); + Console.WriteLine(line); + messageQueue.Enqueue(line); + } + }); + } + + var standardOutput = new StringBuilder(); + var standardError = new StringBuilder(); + LogStream(process.StandardOutput, standardOutput); + LogStream(process.StandardError, standardError); + + var title = $"Compiling: {assetPath}"; + + EditorUtility.DisplayProgressBar(title, title, 0f); + try + { + while (!process.HasExited) + { + string? message = null; + while (messageQueue.TryDequeue(out var dequeued)) message = dequeued; + + if (message != null) EditorUtility.DisplayProgressBar(title, message, 0f); + + Thread.Sleep(100); + } + } + finally + { + EditorUtility.ClearProgressBar(); + } + + if (process.ExitCode != 0) + { + ctx.LogImportError($"Error importing {assetPath}: \n{standardError}\n\nstdout:\n{standardOutput}"); + return; + } + + // TODO: get artifact path from stdout + + var outPath = $"Library/com.ruccho.waas/rust/target/wasm32-unknown-unknown/release/waas_{hash}.wasm"; + var depInfoPath = $"Library/com.ruccho.waas/rust/target/wasm32-unknown-unknown/release/waas_{hash}.d"; + + var depInfo = File.ReadAllText(depInfoPath); + + static ReadOnlySpan ReadSegment(ref ReadOnlySpan content) + { + for (var i = 0; i < content.Length; i++) + { + var c = content[i]; + switch (c) + { + case '\\': + { + // escaped space + i++; + continue; + } + case ' ' or '\n': + { + var segment = content[..i]; + content = content[(i + 1)..]; + return segment; + } + } + } + + return content; + } + + var span = depInfo.AsSpan(); + var found = false; + while (span.Length > 0) + { + var segment = ReadSegment(ref span).ToString(); + segment = segment.Replace("\\ ", " "); + segment = segment.Replace("\\", "/"); + if (segment[^1] == ':') + { + if (segment.AsSpan()[..^1].SequenceEqual(outPath.AsSpan())) + found = true; + else + found = false; + } + else if (found && segment != assetPath) + { + ctx.DependsOnSourceAsset(segment); + } + } + + var moduleBytes = File.ReadAllBytes(outPath); + + if (settings.ComponentizationSettings is { } componentizationSettings) + { + var componentBytes = NativeMethods.Componentize(moduleBytes, componentizationSettings.WitDirectory, + componentizationSettings.World ?? "", StringEncoding.UTF8); + var component = ScriptableObject.CreateInstance(); + component.SetData(componentBytes); + ctx.AddObjectToAsset("component", component); + ctx.SetMainObject(component); + } + else + { + var module = ScriptableObject.CreateInstance(); + module.SetData(moduleBytes); + ctx.AddObjectToAsset("module", module); + ctx.SetMainObject(module); + } + } + + private static void EnsureWorkspaceCreated() + { + if (File.Exists("Cargo.toml")) return; + File.WriteAllText("Cargo.toml", + "[workspace]\nmembers = [\"Library/com.ruccho.waas/rust/crates/*\"] # Used by WaaS\nresolver = \"2\" # Used by WaaS\n", + Encoding.UTF8); + } + + [MenuItem("Assets/Recompile Rust scripts for WaaS")] + private static void Reimport() + { + foreach (var guid in AssetDatabase.FindAssets(@"glob:""**/*.rs""")) + AssetDatabase.ImportAsset(AssetDatabase.GUIDToAssetPath(guid)); + } + + [CustomEditor(typeof(RustImporter))] + private class Editor : ScriptedImporterEditor + { + public override void OnInspectorGUI() + { + EditorGUI.BeginChangeCheck(); + + var crateRootProp = serializedObject.FindProperty(nameof(crateRoot)); + EditorGUILayout.PropertyField(crateRootProp); + + if (crateRootProp.boolValue) + { + var presetProp = serializedObject.FindProperty(nameof(preset)); + EditorGUILayout.PropertyField(presetProp); + + if (presetProp.objectReferenceValue == null) + { + var settingsProp = serializedObject.FindProperty(nameof(settings)).Copy(); + var initialDepth = settingsProp.depth; + if (settingsProp.NextVisible(true)) + do + { + if (settingsProp.depth <= initialDepth) break; + EditorGUILayout.PropertyField(settingsProp); + } while (settingsProp.NextVisible(false)); + } + } + + if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties(); + + ApplyRevertGUI(); + } + } + } +} +#endif \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/RustImporter.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporter.cs.meta similarity index 100% rename from WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/RustImporter.cs.meta rename to WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporter.cs.meta diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPreset.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPreset.cs new file mode 100644 index 0000000..eace63c --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPreset.cs @@ -0,0 +1,10 @@ +using UnityEngine; + +namespace WaaS.Unity.Editor.Rust +{ + public class RustImporterPreset : ScriptableObject + { + [SerializeField] internal RustImporterSettings settings; + public IRustImporterSettings Settings => settings; + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPreset.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPreset.cs.meta new file mode 100644 index 0000000..84062ed --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPreset.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ed0a7a115d5b4d49b40037a0d8c3b45c +timeCreated: 1733626127 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPresetImporter.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPresetImporter.cs new file mode 100644 index 0000000..f3f187e --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPresetImporter.cs @@ -0,0 +1,27 @@ +using UnityEditor; +using UnityEditor.AssetImporters; +using UnityEngine; + +namespace WaaS.Unity.Editor.Rust +{ + [ScriptedImporter(1, "rustimporterpreset", -100)] + public class RustImporterPresetImporter : ScriptedImporter + { + [SerializeField] private RustImporterSettings settings; + + public override void OnImportAsset(AssetImportContext ctx) + { + var preset = ScriptableObject.CreateInstance(); + preset.settings = settings; + ctx.AddObjectToAsset("preset", preset); + + ctx.SetMainObject(preset); + } + + [MenuItem("Assets/Create/WaaS/Rust Importer Preset")] + private static void CreatePreset() + { + ProjectWindowUtil.CreateAssetWithContent("New Rust Importer Preset.rustimporterpreset", ""); + } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPresetImporter.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPresetImporter.cs.meta new file mode 100644 index 0000000..42ed633 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterPresetImporter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f90bfd9334e241909e8cfc9612ef7ff7 +timeCreated: 1733664755 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterSettings.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterSettings.cs new file mode 100644 index 0000000..f50ae0f --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterSettings.cs @@ -0,0 +1,75 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace WaaS.Unity.Editor.Rust +{ + [Serializable] + public class RustImporterSettings : IRustImporterSettings + { + [SerializeReference] [ManagedReferenceSelector] + private PackageDependency?[]? dependencies; + + [SerializeField] private bool componentize; + [SerializeField] private ComponentizationSettings? componentizationSettings; + + public IEnumerable Dependencies => + dependencies?.Where(dep => dep != null).Select(dep => dep!) ?? Enumerable.Empty(); + + public ComponentizationSettings? ComponentizationSettings => componentize ? componentizationSettings : null; + + [Serializable] + private abstract class PackageDependency : IRustPackageDependency + { + [SerializeField] private string? name; + [SerializeField] protected VersionRequirement[]? versionRequirement; + [SerializeField] private string[]? features; + + public string Name => name ?? throw new InvalidOperationException(); + + IEnumerable? IRustPackageDependency.VersionRequirements => + versionRequirement?.Length is 0 or null ? null : versionRequirement; + + public string[]? Features => features; + } + + [Serializable] + [ManagedReferenceTypeDisplayName("via Git")] + private class PackageGitDependency : PackageDependency, IRustPackageGitDependency + { + [SerializeField] private string? gitUrl; + [SerializeField] private string? branch; + [SerializeField] private string? tag; + [SerializeField] private string? rev; + + public string GitUrl => gitUrl ?? throw new InvalidOperationException(); + public string? Branch => branch; + public string? Tag => tag; + public string? Rev => rev; + } + + [Serializable] + [ManagedReferenceTypeDisplayName("via Registry")] + private class PackageRegistryDependency : PackageDependency, IRustPackageRegistryDependency + { + [SerializeField] private string? registry; + public string? Registry => string.IsNullOrEmpty(registry) ? null : registry; + + public IEnumerable VersionRequirements => + versionRequirement ?? throw new InvalidOperationException(); + } + + [Serializable] + private class VersionRequirement : IVersionRequirement + { + [SerializeField] private VersionRequirementKind kind; + [SerializeField] private string? version; + + public VersionRequirementKind Kind => kind; + public string Version => version ?? throw new InvalidOperationException(); + } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterSettings.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterSettings.cs.meta new file mode 100644 index 0000000..4881247 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/RustImporterSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: acf2409ec99b403e89555e7ab59ddcfc +timeCreated: 1733627459 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/VersionRequirementKind.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/VersionRequirementKind.cs new file mode 100644 index 0000000..d06265a --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/VersionRequirementKind.cs @@ -0,0 +1,13 @@ +namespace WaaS.Unity.Editor.Rust +{ + public enum VersionRequirementKind + { + Caret, + Tilde, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual, + Equal + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/VersionRequirementKind.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/VersionRequirementKind.cs.meta new file mode 100644 index 0000000..3eff02b --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Rust/VersionRequirementKind.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7c87acfa96544617a548ca192a673a37 +timeCreated: 1733627357 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/RustImporter.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/RustImporter.cs deleted file mode 100644 index 4e0c864..0000000 --- a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/RustImporter.cs +++ /dev/null @@ -1,47 +0,0 @@ -#if !WAAS_DISABLE_RUST_IMPORTER -using System; -using System.Diagnostics; -using System.IO; -using UnityEditor.AssetImporters; -using UnityEngine; -using Debug = UnityEngine.Debug; - -namespace WaaS.Unity.Editor -{ - [ScriptedImporter(1, "rs")] - public class RustImporter : ScriptedImporter - { - public override void OnImportAsset(AssetImportContext ctx) - { - var module = ScriptableObject.CreateInstance(); - - var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.wasm"); - - var process = Process.Start(new ProcessStartInfo(@$"rustc", - $@"""{ctx.assetPath}"" --target wasm32-unknown-unknown --crate-type cdylib -o ""{path}"" -C opt-level=z -C lto") - { - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true - } - ); - process!.WaitForExit(); - - var error = process.StandardError.ReadToEnd(); - - if (!string.IsNullOrEmpty(error)) - { - throw new Exception(error); - } - - module.SetData(File.ReadAllBytes(path)); - - ctx.AddObjectToAsset("module", module); - - ctx.SetMainObject(module); - File.Delete(path); - } - } -} -#endif \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils.meta new file mode 100644 index 0000000..db6c98b --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a722e5fcde444e52ac3cdb1f1f2ac42c +timeCreated: 1733633629 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorAttribute.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorAttribute.cs new file mode 100644 index 0000000..64b0a4c --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorAttribute.cs @@ -0,0 +1,8 @@ +using UnityEngine; + +namespace WaaS.Unity.Editor +{ + internal class ManagedReferenceSelectorAttribute : PropertyAttribute + { + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorAttribute.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorAttribute.cs.meta new file mode 100644 index 0000000..10c5534 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0443f62ffcfa445eae31a175a97f4475 +timeCreated: 1733633805 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorDrawer.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorDrawer.cs new file mode 100644 index 0000000..fedfd76 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorDrawer.cs @@ -0,0 +1,68 @@ +using System; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace WaaS.Unity.Editor +{ + [CustomPropertyDrawer(typeof(ManagedReferenceSelectorAttribute))] + internal class ManagedReferenceSelectorDrawer : PropertyDrawer + { + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + if (property.propertyType == SerializedPropertyType.ManagedReference) + { + var fieldType = property.GetManagedReferenceFieldType(); + var type = property.GetManagedReferenceType(); + var r = new Rect((float)(position.x + (double)EditorGUIUtility.labelWidth + 2.0), position.y, + (float)(position.width - (double)EditorGUIUtility.labelWidth - 2.0), position.height); + + var displayName = type?.GetCustomAttribute()?.Name; + + r.height = EditorGUIUtility.singleLineHeight; + if (GUI.Button(r, displayName ?? type?.FullName?.Replace('+', '.') ?? "", EditorStyles.popup)) + { + var menu = new GenericMenu(); + menu.AddItem( + new GUIContent( + ""), + type == null, + static state => + { + var stateTyped = state as SerializedProperty; + stateTyped!.managedReferenceValue = null; + stateTyped.serializedObject.ApplyModifiedProperties(); + }, + property); + foreach (var derivedType in TypeCache.GetTypesDerivedFrom(fieldType)) + { + if (derivedType.IsAbstract || derivedType.IsInterface) continue; + var derivedTypeDisplayName = + derivedType.GetCustomAttribute()?.Name; + menu.AddItem( + new GUIContent(derivedTypeDisplayName ?? + $"{derivedType.Namespace}/{derivedType.FullName.Substring(derivedType.Namespace.Length + 1)}"), + derivedType == type, + static state => + { + var stateTyped = ((SerializedProperty, Type))state; + var instance = Activator.CreateInstance(stateTyped.Item2); + stateTyped.Item1.managedReferenceValue = instance; + stateTyped.Item1.serializedObject.ApplyModifiedProperties(); + }, + (property, derivedType)); + } + + menu.ShowAsContext(); + } + } + + EditorGUI.PropertyField(position, property, label, true); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return EditorGUI.GetPropertyHeight(property, true); + } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorDrawer.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorDrawer.cs.meta new file mode 100644 index 0000000..620ab8e --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceSelectorDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9d9f68c5f493455fa167c9b847cc60c7 +timeCreated: 1733633831 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceTypeDisplayNameAttribute.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceTypeDisplayNameAttribute.cs new file mode 100644 index 0000000..cac3b56 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceTypeDisplayNameAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace WaaS.Unity.Editor +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] + internal class ManagedReferenceTypeDisplayNameAttribute : Attribute + { + public ManagedReferenceTypeDisplayNameAttribute(string name) + { + Name = name; + } + + public string Name { get; } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceTypeDisplayNameAttribute.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceTypeDisplayNameAttribute.cs.meta new file mode 100644 index 0000000..ec17b85 --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceTypeDisplayNameAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7df3dc2c6de444ee902ebdccbe6d8664 +timeCreated: 1733639680 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceUtility.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceUtility.cs new file mode 100644 index 0000000..a7d8c7d --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceUtility.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; +using UnityEngine.Serialization; + +namespace WaaS.Unity.Editor +{ + internal static class ManagedReferenceUtils + { + private static readonly Dictionary ManagedReferenceExpressionCache = new(); + + public static Type GetManagedReferenceFieldType(this SerializedProperty property) + { + return FromManagedReferenceTypeExpression(property.managedReferenceFieldTypename); + } + + public static Type GetManagedReferenceType(this SerializedProperty property) + { + if (property.managedReferenceId is ManagedReferenceUtility.RefIdNull + or ManagedReferenceUtility.RefIdUnknown) return null; + return FromManagedReferenceTypeExpression(property.managedReferenceFullTypename); + } + + public static Type FromManagedReferenceTypeExpression(string expression) + { + if (string.IsNullOrEmpty(expression)) return null; + if (ManagedReferenceExpressionCache.TryGetValue(expression, out var cachedType)) return cachedType; + + var expressionSpan = expression.AsSpan(); + var space = expressionSpan.IndexOf(' '); + var assemblyName = expressionSpan[..space]; + expressionSpan = expressionSpan[(space + 1)..]; + + Assembly matchedAssembly = null; + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + var name = assembly.GetName(); + if (name.Name.AsSpan().SequenceEqual(assemblyName)) + { + matchedAssembly = assembly; + break; + } + } + + + if (matchedAssembly == null) return null; + + var reader = new TypeNameReader(expressionSpan); + + return ManagedReferenceExpressionCache[expression] = reader.ReadFullName(matchedAssembly); + } + + private ref struct TypeNameReader + { + private ReadOnlySpan sequence; + + public TypeNameReader(ReadOnlySpan expression) + { + sequence = expression; + } + + public Type ReadAssemblyQualifiedName() + { + var typeName = ReadFullName(out var typeArguments); + if (sequence.Length == 0 || sequence[0] != ',') throw new ArgumentException(nameof(sequence)); + + sequence = sequence[1..].TrimStart(' '); + var nextComma = sequence.IndexOf(','); + if (nextComma < 0) throw new ArgumentException(nameof(sequence)); + var assemblyName = sequence[..nextComma]; + + Assembly matchedAssembly = null; + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + if (assembly.GetName().Name.AsSpan().SequenceEqual(assemblyName)) + { + matchedAssembly = assembly; + break; + } + + if (matchedAssembly == null) return null; + + var endBrackets = sequence.IndexOf(']'); + if (endBrackets < 0) + sequence = default; // empty + else + sequence = sequence[endBrackets..]; + + var type = matchedAssembly.GetType(typeName.ToString()); + if (typeArguments is { Length: > 0 }) return type.MakeGenericType(typeArguments); + + return type; + } + + public Type ReadFullName(Assembly assembly) + { + var typeName = ReadFullName(out var typeArguments); + var type = assembly.GetType(typeName.ToString()); + if (typeArguments is { Length: > 0 }) return type.MakeGenericType(typeArguments); + + return type; + } + + public ReadOnlySpan ReadFullName(out Type[] typeArguments) + { + var bracket = sequence.IndexOf('['); + ReadOnlySpan result; + if (bracket >= 0) + { + result = sequence[..bracket]; + sequence = sequence[(bracket + 1)..]; + + List typeArgumentsList = new(); + + while (true) + { + if (sequence.Length == 0) throw new ArgumentException(nameof(sequence)); + + var first = sequence[0]; + + sequence = sequence[1..]; + + switch (first) + { + case '[': + typeArgumentsList.Add(ReadAssemblyQualifiedName() ?? + throw new InvalidOperationException()); + if (sequence.Length == 0 || sequence[0] != ']') + throw new ArgumentException(nameof(sequence)); + sequence = sequence[1..]; + if (sequence.Length >= 1 && sequence[0] == ',') sequence = sequence[1..]; + sequence = sequence.TrimStart(); + break; + case ']': + goto END; + } + } + + END: + typeArguments = typeArgumentsList.ToArray(); + } + else + { + var comma = sequence.IndexOf(','); + if (comma < 0) + { + result = sequence; + sequence = default; + } + else + { + result = sequence[..comma]; + sequence = sequence[comma..]; + } + + typeArguments = Array.Empty(); + } + + + return result; + } + } + } +} \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceUtility.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceUtility.cs.meta new file mode 100644 index 0000000..55ad8bf --- /dev/null +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/Utils/ManagedReferenceUtility.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f5caa56429fb4b5090a20e70d64c5715 +timeCreated: 1733634370 \ No newline at end of file diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/WasmImporter.cs b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/WasmImporter.cs index 95a2d44..432450a 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/WasmImporter.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Editor/Scripts/WasmImporter.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using UnityEditor.AssetImporters; using UnityEngine; using WaaS.Models; diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ComponentAsset.cs b/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ComponentAsset.cs index 00da9b6..d6ff5c2 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ComponentAsset.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ComponentAsset.cs @@ -1,25 +1,24 @@ using System; using System.Threading.Tasks; using UnityEngine; +using WaaS.ComponentModel.Models; using WaaS.ComponentModel.Runtime; -using WaaS.Models; namespace WaaS.Unity { public class ComponentAsset : ScriptableObject { [SerializeField] private bool deserializeOnLoad; - [SerializeField, HideInInspector] private byte[] data; + [SerializeField] [HideInInspector] private byte[] data; [NonSerialized] private Lazy component; + internal ulong Size => (ulong)(data?.Length ?? 0); + private void OnEnable() { - component = new(() => ComponentModel.Models.Component.Create(data), true); - - if (deserializeOnLoad && data != null) - { - LoadComponent(); - } + component = new Lazy(() => UnresolvedComponent.Create(data), true); + + if (deserializeOnLoad && data != null) LoadComponent(); } private void OnDisable() diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ComponentAsset.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ComponentAsset.cs.meta index fd52a4a..6d7256e 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ComponentAsset.cs.meta +++ b/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ComponentAsset.cs.meta @@ -1,3 +1,11 @@ -fileFormatVersion: 2 +fileFormatVersion: 2 guid: 24e486694fdc4b5788f2e379697dba06 -timeCreated: 1728915439 \ No newline at end of file +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 470c37360287f0d4697fc18fdd47c97a, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ModuleAsset.cs b/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ModuleAsset.cs index f675735..08d4502 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ModuleAsset.cs +++ b/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ModuleAsset.cs @@ -11,6 +11,8 @@ public class ModuleAsset : ScriptableObject [SerializeField, HideInInspector] private byte[] data; [NonSerialized] private Lazy module; + internal ulong Size => (ulong)(data?.Length ?? 0); + private void OnEnable() { module = new(() => diff --git a/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ModuleAsset.cs.meta b/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ModuleAsset.cs.meta index 4e3d245..29dd627 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ModuleAsset.cs.meta +++ b/WaaS.Unity/Packages/com.ruccho.waas/Runtime/Scripts/ModuleAsset.cs.meta @@ -5,7 +5,7 @@ MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 - icon: {instanceID: 0} + icon: {fileID: 2800000, guid: 470c37360287f0d4697fc18fdd47c97a, type: 3} userData: assetBundleName: assetBundleVariant: diff --git a/WaaS.Unity/Packages/com.ruccho.waas/package.json b/WaaS.Unity/Packages/com.ruccho.waas/package.json index 088a9ae..909dfcf 100644 --- a/WaaS.Unity/Packages/com.ruccho.waas/package.json +++ b/WaaS.Unity/Packages/com.ruccho.waas/package.json @@ -1,6 +1,6 @@ { "name": "com.ruccho.waas", - "version": "0.1.1", + "version": "0.2.0", "displayName": "WaaS", "description": "Stands for WebAssembly as a Script, a language-independent scripting engine for Unity and .NET.", "unity": "2022.3", diff --git a/WaaS.Unity/wit/world.wit b/WaaS.Unity/wit/world.wit new file mode 100644 index 0000000..adfface --- /dev/null +++ b/WaaS.Unity/wit/world.wit @@ -0,0 +1,10 @@ +package my-game:my-sequencer; + +world sequence { + import env; + export play: func(); +} + +interface env { + show-message: func(speaker: string, message: string); +} \ No newline at end of file diff --git a/WaaS/waas-unity-native-editor/.gitignore b/WaaS/waas-unity-native-editor/.gitignore new file mode 100644 index 0000000..ee21b8f --- /dev/null +++ b/WaaS/waas-unity-native-editor/.gitignore @@ -0,0 +1,21 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ \ No newline at end of file diff --git a/WaaS/waas-unity-native-editor/Cargo.toml b/WaaS/waas-unity-native-editor/Cargo.toml new file mode 100644 index 0000000..91b7577 --- /dev/null +++ b/WaaS/waas-unity-native-editor/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "waas-unity-native-editor" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-component = "0.221.2" +wit-parser = "0.221.2" \ No newline at end of file diff --git a/WaaS/waas-unity-native-editor/src/lib.rs b/WaaS/waas-unity-native-editor/src/lib.rs new file mode 100644 index 0000000..99b4cc4 --- /dev/null +++ b/WaaS/waas-unity-native-editor/src/lib.rs @@ -0,0 +1,67 @@ +use std::error::Error; +use std::ffi::{c_char, CStr}; +use wit_component::ComponentEncoder; +use wit_parser::Resolve; + +#[no_mangle] +pub unsafe extern "C" fn componentize( + source: *const u8, + source_len: usize, + wit_path: *const c_char, + world: *const c_char, + encoding: StringEncoding, + user_data: usize, + on_success: extern "C" fn(usize, *const u8, usize), + on_error: extern "C" fn(usize, *const c_char), +) { + let result = componentize_inner(source, source_len, wit_path, world, encoding); + match result { + Ok(bytes) => on_success(user_data, bytes.as_ptr(), bytes.len()), + Err(e) => { + let error = std::ffi::CString::new(e.to_string()).unwrap(); + on_error(user_data, error.as_ptr()); + } + } +} + +unsafe fn componentize_inner( + source: *const u8, + source_len: usize, + wit_path: *const c_char, + world: *const c_char, + encoding: StringEncoding, +) -> Result, Box> { + let wit_path = CStr::from_ptr(wit_path) + .to_str()?; + let world = CStr::from_ptr(world).to_str()?; + let mut resolve = Resolve::default(); + let (id, _) = resolve.push_dir(wit_path)?; + let world = resolve + .select_world(id, if world.is_empty() { None } else { Some(world) })?; + + let mut wasm = std::slice::from_raw_parts(source, source_len).to_vec(); + + wit_component::embed_component_metadata( + &mut wasm, + &resolve, + world, + match encoding { + StringEncoding::UTF8 => wit_component::StringEncoding::UTF8, + StringEncoding::UTF16 => wit_component::StringEncoding::UTF16, + StringEncoding::CompactUTF16 => wit_component::StringEncoding::CompactUTF16, + }, + )?; + + let mut encoder = ComponentEncoder::default(); + encoder = encoder.module(&wasm)?; + + let result = encoder.encode()?; + Ok(result) +} + +#[repr(u32)] +pub enum StringEncoding { + UTF8, + UTF16, + CompactUTF16, +} \ No newline at end of file diff --git a/WaaS/wit2waas/Cargo.toml b/WaaS/wit2waas/Cargo.toml index 2dc31a4..940e643 100644 --- a/WaaS/wit2waas/Cargo.toml +++ b/WaaS/wit2waas/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wit2waas" -version = "0.1.1" +version = "0.2.0" edition = "2021" authors = ["ruccho"] repository = "https://github.com/ruccho/WaaS" diff --git a/docs/docs/component-model/index.mdx b/docs/docs/component-model/index.mdx index b576f47..0c0e013 100644 --- a/docs/docs/component-model/index.mdx +++ b/docs/docs/component-model/index.mdx @@ -6,7 +6,7 @@ sidebar_position: 4 import DocCardList from '@theme/DocCardList'; :::info -About an overview of Component Model, see [Component Model Tutorial](./../getting-started/component-model.md). +About an overview of Component Model, see [Component Model Tutorial](./../getting-started/component-model). ::: ### Component Model Workflow diff --git a/docs/docs/getting-started/component-model.md b/docs/docs/getting-started/component-model.md deleted file mode 100644 index 51c2d49..0000000 --- a/docs/docs/getting-started/component-model.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -title: Component Model Tutorial -sidebar_position: 3 ---- - -With the Component Model API of WaaS, you can run components based on the [WebAssembly Component Model](https://component-model.bytecodealliance.org/). - -While traditional WebAssembly modules were limited to numeric types for arguments and return values, the Component Model allows for more complex data types such as strings, lists, and structures to be exchanged. - -### 1. Creating WIT - -In the Component Model, you define types and function signatures in advance using an IDL called [**WIT**](https://component-model.bytecodealliance.org/design/wit.html) (WebAssembly Interface Type). The following is an example of a WIT intended for use in a simple conversation scene: - -```wit -package my-game:my-sequencer; - -world sequence { - import env; - export play: func(); -} - -interface env { - show-message: func(speaker: string, message: string); - show-options: func(options: list) -> u32; -} -``` - -`world` defines the set of functions to import and export for this WebAssembly component. `interface env` defines the set of functions to be implemented on the host environment side and imported into the component. - -### 2. Guest Language Work - -Once you have created the WIT, generate bindings for each guest language from the WIT. Use the tool [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) for this purpose. Currently, it supports generating bindings for Rust, C, Java, Go, C#, and Moonbit. For Rust, a tool called [`cargo-component`](https://github.com/bytecodealliance/cargo-component) is available to incorporate code generation from WIT into the build pipeline, so we will use it this time. - -```sh -cargo install cargo-component --locked -``` - -Create a new Rust workspace. - -```sh -cargo component new hello-world --lib -``` - -Place the WIT from earlier in `wit/world.wit`. - -The following is an example of a script written using the generated bindings for Rust. It calls the functions defined in `interface env`. - -```rust -#[allow(warnings)] -mod bindings; - -use bindings::my_game::my_sequencer::env::*; -use bindings::Guest; - -struct Component; - -impl Guest for Component { - fn play() { - show_message("Sigmo", "Would you mind bringing me a battery?"); - match show_options(&["Sure".to_string(), "No".to_string()]) { - 0 => show_message("Sigmo", "Thank you!"), - 1 => show_message("Sigmo", "……"), - _ => {} - } - show_message("Sigmo", "……"); - } -} - -bindings::export!(Component with_types_in bindings); -``` - -With `cargo-component`, the following command generates bindings from WIT and creates the component: - -```sh -cargo component build --release --target wasm32-unknown-unknown -``` - -The result will be output to `target/wasm32-unknown-unknown/release/hello_world.wasm`. Import this into your Unity project. - -### 3. C# Work - -Now, before you can actually run the component, you need to implement `show-message` and `show-options` on the C# side. To do this, you first need to convert the WIT to C# interfaces using the `wit2waas` tool provided by WaaS. - -```sh -cargo install wit2waas -``` - -Run `wit2waas` in the directory where the `wit` directory is located. - -```sh -wit2waas -``` - -This will generate the following C# code from the WIT above: - -```csharp -// -#nullable enable - -namespace MyGame.MySequencer -{ - [global::WaaS.ComponentModel.Binding.ComponentInterface(@"env")] - public partial interface IEnv - { - [global::WaaS.ComponentModel.Binding.ComponentApi(@"show-message")] - global::System.Threading.Tasks.ValueTask ShowMessage(string @speaker, string @message); - - [global::WaaS.ComponentModel.Binding.ComponentApi(@"show-options")] - global::System.Threading.Tasks.ValueTask ShowOptions(global::System.ReadOnlyMemory @options); - - } -} -``` - -There are various attributes attached to the generated code, which allow the Source Generator to generate the code that actually exchanges values between the WebAssembly component and C#. - -Then implement the `IEnv`. - -```csharp -class Env : IEnv -{ - public static readonly Env Instance = new(); - - public async ValueTask ShowMessage(string speaker, string message) - { - Debug.Log($"{speaker}: {message}"); - } - - public async ValueTask ShowOptions(ReadOnlyMemory options) - { - Debug.Log($"Options: {string.Join(", ", options.ToArray())}"); - return 0; - } -} -``` - -Now it's time to run the component. Pass an instance of a class that implements `IEnv` to the component, and `ShowMessage()` and `ShowOptions()` will be called from within the component. - -```csharp -[SerializeField] private ComponentAsset componentAsset; - -var component = componentAsset.LoadComponent(); - -// Instantiate the component -var instance = component.Instantiate(null, new Dictionary() -{ - // Import the `env` implementation - { "my-game:my-sequencer/env", IEnv.CreateWaaSInstance(Env.Instance) } -}); - -using var context = new ExecutionContext(); -var wrapper = new ISequence.Wrapper(instance, context); - -// Run -await wrapper.Play(); -``` - - diff --git a/docs/docs/getting-started/component-model.mdx b/docs/docs/getting-started/component-model.mdx new file mode 100644 index 0000000..01c8234 --- /dev/null +++ b/docs/docs/getting-started/component-model.mdx @@ -0,0 +1,178 @@ +--- +title: Component Model Tutorial +sidebar_position: 3 +--- + +import rustImporterComponentSettingsImage from '@site/static/img/rust-importer-component-settings.png'; +import rustImporterImportedComponentImage from '@site/static/img/rust-importer-imported-component.png'; + +With the Component Model API of WaaS, you can run components based on the [WebAssembly Component Model](https://component-model.bytecodealliance.org/). + +While traditional WebAssembly modules were limited to numeric types for arguments and return values, the Component Model allows for more complex data types such as strings, lists, and structures to be exchanged. + +This tutorial explains how to create a component using Rust and run it in Unity. + +### 1. Installing Required Tools + +Install the [Rust toolchain](https://www.rust-lang.org/learn/get-started) and add the `wasm32-unknown-unknown` target. + +```sh +rustup target add wasm32-unknown-unknown +``` + +### 2. Creating WIT + +In the Component Model, you define types and function signatures in advance using an IDL called [**WIT**](https://component-model.bytecodealliance.org/design/wit.html) (WebAssembly Interface Type). + +First, create `wit/sequence.wit` under the root of your Unity project. +The following is an example of a WIT intended for use in a simple conversation scene: + +```wit +package my-game:my-sequencer; + +world sequence { + import env; + export play: func(); +} + +interface env { + show-message: func(speaker: string, message: string); +} +``` + +`world` defines the set of functions to import and export for this WebAssembly component. `interface env` defines the set of functions to be implemented on the host environment side and imported into the component. + +### 2. Creating Rust Source Files + +To create a component in Rust, you need to generate bindings for Rust from the WIT file you created earlier. Use the tool [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) for this purpose. The usage varies depending on the language you are using, but this time we will introduce how to use it in Rust. + +Create `Assets/sequence_0.rs` in any location under `Assets`. + +```rust +use crate::my_game::my_sequencer::env::show_message; + +wit_bindgen::generate!({ + path: "../../../../../wit", + world: "my_game::my_sequencer/sequnce" +}); + +struct Sequence; + +impl Guest for Sequence { + fn play() { + show_message("ぼく", "こんにちは!"); + } +} + +export!(Sequence); +``` + +:::info +The `wit_bindgen::generate!` macro generates bindings from the WIT file. +`wit-bindgen` tries to find the WIT file relative to the `Cargo.toml` file actually located at `Library/com.ruccho.waas/rust/crates/~~~/Cargo.toml` when using WaaS, not the Rust source file. The path `../../../../../wit` is used to refer to the project root from this `Cargo.toml`. +Refer to [Using Rust Importer](../unity-integration/rust) for more information. +::: + +### 3. Importing and Configuring + +Import the Rust source file you created earlier into Unity. + +
+ +
+ +- Enable **Crate Root** +- Add a dependency "via Registry" under **Dependencies** and specify `wit-bindgen` in Name +- Enable **Componentize** + - Set the **Wit Directory** to `wit` + - Set the **Component Name** to `my-game:my-sequencer/sequence` + +:::info +Refer to [Using Rust Importer](../unity-integration/rust) for more information on the settings. +::: + +Click **Apply** to import. Once the compilation and componentization are complete, the component information will be displayed in the Inspector. + +
+ +
+ +We can see that the function `play` is exported from the component. + +### 4. Generating C# Bindings + +To easily run the component you created, let's create C# bindings. WaaS provides a CLI tool called `wit2waas` that generates C# interfaces from the WIT file. + +In the root of your Unity project, run [`wit2waas`](../component-model/wit2waas). + +```sh +cargo install wit2waas +wit2waas --out "Assets/WaaS Generated" +``` + +It will generate the following C# code under `Assets/WaaS Generated/my-game/my-sequencer` from the WIT above: + +```csharp +// +#nullable enable + +namespace MyGame.MySequencer +{ + // interface env + [global::WaaS.ComponentModel.Binding.ComponentInterface(@"env")] + public partial interface IEnv + { + [global::WaaS.ComponentModel.Binding.ComponentApi(@"show-message")] + global::System.Threading.Tasks.ValueTask ShowMessage(string @speaker, string @message); + + } +} +``` + +There are various attributes attached to the generated code, which allow the Source Generator to generate the code that actually exchanges values between the WebAssembly component and C#. + +`IEnv` is an interface that defines the functions to be implemented on the host environment side. Then implement the `IEnv`. + +```csharp +internal class Env : IEnv +{ + public ValueTask ShowMessage(string speaker, string message) + { + Debug.Log($"{speaker}「{message}」"); + return default; + } +} +``` + +### 5. Running the Component + +Now it's time to run the component. + +```csharp +using System.Collections.Generic; +using MyGame.MySequencer; +using UnityEngine; +using WaaS.ComponentModel.Runtime; +using WaaS.Runtime; +using WaaS.Unity; + +public class RunSequenceTest : MonoBehaviour +{ + [SerializeField] private ComponentAsset componentAsset; + + private async void Start() + { + var component = await componentAsset.LoadComponentAsync(); + var instance = component.Instantiate(new Dictionary() + { + { "my-game:my-sequencer/env", IEnv.CreateWaaSInstance(new Env()) } + }); + using var context = new ExecutionContext(); + var sequence = new ISequence.Wrapper(instance, context); + await sequence.Play(); // ぼく「こんにちは!」 + } +} +``` + +Attach `RunSequenceTest` to a GameObject and assign the imported component to `Component Asset`. +Now you can run the component in Unity. diff --git a/docs/docs/getting-started/installation.md b/docs/docs/getting-started/installation.mdx similarity index 100% rename from docs/docs/getting-started/installation.md rename to docs/docs/getting-started/installation.mdx diff --git a/docs/docs/getting-started/unity-rust.md b/docs/docs/getting-started/unity-rust.mdx similarity index 88% rename from docs/docs/getting-started/unity-rust.md rename to docs/docs/getting-started/unity-rust.mdx index ad0b571..1035eba 100644 --- a/docs/docs/getting-started/unity-rust.md +++ b/docs/docs/getting-started/unity-rust.mdx @@ -7,13 +7,13 @@ In this tutorial, we will compile Rust to WebAssembly and run it in Unity. WaaS can import WebAssembly modules, but you can also import Rust files directly and compile them into WebAssembly modules on the spot. This method does not allow you to manage dependencies with `cargo`. -#### 1. Install [Rust toolchain](https://www.rust-lang.org/ja/learn/get-started) and add the `wasm32-unknown-unknown` target. +#### 1. Install [Rust toolchain](https://www.rust-lang.org/learn/get-started) and add the `wasm32-unknown-unknown` target. ```sh rustup target add wasm32-unknown-unknown ``` -#### 2. [Install WaaS](./installation.md) in your Unity project. +#### 2. [Install WaaS](./installation.mdx) in your Unity project. #### 3. Create `main.rs` in your Unity project: diff --git a/docs/docs/unity-integration/index.mdx b/docs/docs/unity-integration/index.mdx new file mode 100644 index 0000000..68c092a --- /dev/null +++ b/docs/docs/unity-integration/index.mdx @@ -0,0 +1,9 @@ +--- +title: Unity Integration +sidebar_position: 5 +--- +import DocCardList from '@theme/DocCardList'; + +WaaS provides features to improve the workflow in Unity for each guest language. If you have any requests for additional languages, please search or create a new one in [Issues](https://github.com/ruccho/WaaS/issues). + + \ No newline at end of file diff --git a/docs/docs/unity-integration/rust.mdx b/docs/docs/unity-integration/rust.mdx new file mode 100644 index 0000000..e8868a5 --- /dev/null +++ b/docs/docs/unity-integration/rust.mdx @@ -0,0 +1,123 @@ +--- +title: Rust +sidebar_position: 1 +--- + +import Badge from '@site/src/components/Badge'; +import rustImporterImage from '@site/static/img/rust-importer.png'; + +# Using Rust Importer + +WaaS allows you to directly compile `*.rs` files to WebAssembly by importing them as assets. + +:::info +To use Rust Importer, you need the [Rust toolchain](https://www.rust-lang.org/tools/install) and the [`wasm32-unknown-unknown`](https://doc.rust-lang.org/rustc/platform-support/wasm32-unknown-unknown.html#building-rust-programs) target. +::: + +## Import Settings + +
+ +
+ +- **Create Root**: Specify whether to treat the source file as the root of the crate. If unchecked, the source file will not be compiled directly to WebAssembly, but can be referenced as a module from other source files. +- **Dependencies**: Specify the packages to depend on. + - **via Registry**: Install packages from the package registry. + - **via Git**: Install packages from a Git repository. +- **Componentize**: Convert the WebAssembly module to a component. See [Componentization](#componentization) for details. + +### Dependency + +- **Name**: Specify the package name. +- **Version Requirements**: Specify the version. See [The Cargo Book](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html) for details. + +### via Registry Specific Items + +- **Registry** (optional): Specify the registry to get the package from. See [The Cargo Book](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-other-registries) for details. + +### via Git Specific Items + +- **Git Url**: Specify the URL of the Git repository. +- **Branch** (optional): Specify the branch name. +- **Tag** (optional): Specify the tag name or commit hash. +- **Rev** (optional): Specify the commit hash. + +:::warning +Rust Importer automatically recompiles when it detects changes in the source file. However, changes are not detected in the following cases: + +- When the dependent source file is outside of Unity's management +- When dependent source file does not exist, and the corresponding source file is created or modified after a compilation error +- When it depends on files other than `.rs` + +In these cases, right-click the target source file and select `Reimport` to recompile it. +Also, selecting `Assets` > `Recompile Rust scripts for WaaS` recompiles all Rust source files in the project. +::: + +### Using Presets + +You can save the above settings as a **Preset Asset** for reuse. + +Crate a preset asset with `Create` > `WaaS` > `Rust Importer Preset` then set it to the **Preset** field in the Rust Importer settings. + +## Using IDE functionality + +Open the root folder of the project as a workspace with the IDE which supports Rust, and you can use the IDE's features such as code completion and syntax highlighting. + +WaaS acomplishes this by generating a `Cargo.toml` file in the project root directory: + +```toml +[workspace] +members = ["Library/com.ruccho.waas/rust/crates/*"] # Used by WaaS +resolver = "2" # Used by WaaS + +``` + +Internally, Rust Importer generates crates under `Library/com.ruccho.waas/rust/crates/*` for each source file with **Crate Root** checked. `Cargo.toml` files generated for each crate cannot be modified by hand because they are overwritten when the Rust Importer settings are changed. + +## Disabling Rust Importer + +To disable Rust Importer, use `WAAS_DISABLE_RUST_IMPORTER` define symbol. + +## Componentization + +:::info +Refer to [Component Model Tutorial](../getting-started/component-model) for more information on componentization. +::: + +## Best Practices + +### Version Control + +It is recommended to add `Cargo.toml` and `Cargo.lock` at the root of the project to version control. + +### Using wee_alloc + +[wee_alloc](https://github.com/rustwasm/wee_alloc) is an allocator for WebAssembly with small code size. + +Add `wee_alloc` to the dependencies and write the following: + +```rust +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; +``` + +### Build Settings + +We can add options to optimize the code size to `Cargo.toml` at the root of the project: + +```toml +## ... + +[profile.release] +lto = true +codegen-units = 1 +opt-level = "z" +``` + +:::info +Currently, Rust Importer always uses `--release` profile to compile. +::: + + + + diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index e0a69fc..d4680b6 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -88,7 +88,7 @@ const config: Config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, - additionalLanguages: ['csharp', 'bash'], + additionalLanguages: ['csharp', 'bash', 'toml'], }, colorMode: { defaultMode: 'dark', diff --git a/docs/i18n/ja/docusaurus-plugin-content-docs/current/component-model/index.mdx b/docs/i18n/ja/docusaurus-plugin-content-docs/current/component-model/index.mdx index 454dea9..ff300ed 100644 --- a/docs/i18n/ja/docusaurus-plugin-content-docs/current/component-model/index.mdx +++ b/docs/i18n/ja/docusaurus-plugin-content-docs/current/component-model/index.mdx @@ -6,7 +6,7 @@ sidebar_position: 4 import DocCardList from '@theme/DocCardList'; :::info -Component Model についての概要は 「[Component Model ではじめる](./../getting-started/component-model.md)」 も参照してください。 +Component Model についての概要は 「[Component Model ではじめる](./../getting-started/component-model.mdx)」 も参照してください。 ::: ### Component Model のワークフロー diff --git a/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/component-model.md b/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/component-model.md deleted file mode 100644 index b88951e..0000000 --- a/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/component-model.md +++ /dev/null @@ -1,157 +0,0 @@ ---- -title: Component Model ではじめる -sidebar_position: 3 ---- - -WaaS の **Component Model API** を使用すると、[WebAssembly Component Model](https://component-model.bytecodealliance.org/) に基づくコンポーネントを実行できます。 - -通常の WebAssembly モジュールでは引数や戻り値の型が数値に限られていましたが、Component Model では、文字列やリスト、構造体などより複雑な型を持つデータをやり取りすることができます。 - -### 1. WITの作成 - -Component Model では、[**WIT**](https://component-model.bytecodealliance.org/design/wit.html) (WebAssembly Interface Type) という IDL を使って型や関数のシグネチャを事前に定義します。 -以下は簡単な会話シーンで使うことを想定した WIT の例です。 - -```wit -package my-game:my-sequencer; - -world sequence { - import env; - export play: func(); -} - -interface env { - show-message: func(speaker: string, message: string); - show-options: func(options: list) -> u32; -} -``` - -`world` はこの WebAssembly コンポーネントのインポート・エクスポートする機能のセットを定義するものです。`interface env` はホスト環境側で実装しコンポーネントにインポートする機能のセットです。 - -### 2. ゲスト言語側の作業 - -WITが作成できたら、WITから各ゲスト言語用のバインディングを生成します。これには [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) というツールを使います。現在は Rust, C, Java, Go, C#, Moonbit 向けのバインディング生成に対応しています。Rust 向けには、WIT からのコード生成をビルドパイプラインに組み込む [`cargo-component`](https://github.com/bytecodealliance/cargo-component) というツールが公開されているので、今回はこちらを使います。 - -``` -cargo install cargo-component --locked -``` - -新しい Rust ワークスペースを作成します。 - -``` -cargo component new hello-world --lib -``` - -先ほどの WIT を `wit/world.wit` に置いておきます。 - -以下は、Rust 用に生成されたバインディングを利用して書かれたスクリプトの例です。`interface env` に定義した関数を呼び出しています。 - -```rust -#[allow(warnings)] -mod bindings; - -use bindings::my_game::my_sequencer::env::*; -use bindings::Guest; - -struct Component; - -impl Guest for Component { - fn play() { - show_message("シグモ", "よければ……バッテリーを持ってきてくれないですか"); - match show_options(&["いいよ".to_string(), "だめ".to_string()]) { - 0 => show_message("シグモ", "ありがとう……"), - 1 => show_message("シグモ", "えっ……"), - _ => {} - } - show_message("シグモ", "……"); - } -} - -bindings::export!(Component with_types_in bindings); -``` - -`cargo-component` では、以下のコマンドでWITからのバインディング生成とコンポーネントの作成まで実行されます。 - -``` -cargo component build --release --target wasm32-unknown-unknown -``` - -結果は `target/wasm32-unknown-unknown/release/hello_world.wasm` に出力されます。こちらを Unity プロジェクトにインポートしておきます。 - -### 3. C#側の作業 - -さて、コンポーネントを実際に動かす前に、`show-message` と `show-options` を C# 側で実装する必要があります。これにはまず WaaS で提供している `wit2waas` ツールを使用して、WIT を C# のインターフェースに変換します。 - -``` -cargo install wit2waas -``` - -`wit` ディレクトリがある場所で `wit2waas` を実行します。 - -```sh -wit2waas -``` - -すると、先ほどの WIT から次のような C# コードが生成されます。 - -```cs -// -#nullable enable - -namespace MyGame.MySequencer -{ - [global::WaaS.ComponentModel.Binding.ComponentInterface(@"env")] - public partial interface IEnv - { - [global::WaaS.ComponentModel.Binding.ComponentApi(@"show-message")] - global::System.Threading.Tasks.ValueTask ShowMessage(string @speaker, string @message); - - [global::WaaS.ComponentModel.Binding.ComponentApi(@"show-options")] - global::System.Threading.Tasks.ValueTask ShowOptions(global::System.ReadOnlyMemory @options); - - } -} -``` - -いろいろと属性がついていますが、これによって Source Generator が具体的に WebAssembly コンポーネントと値をやり取りするためのコードを生成します。 - -次は、`IEnv` を実装しておきます。 - -```cs -class Env : IEnv -{ - public static readonly Env Instance = new(); - - public async ValueTask ShowMessage(string speaker, string message) - { - Debug.Log($"{speaker}: {message}"); - } - - public async ValueTask ShowOptions(ReadOnlyMemory options) - { - Debug.Log($"Options: {string.Join(", ", options.ToArray())}"); - return 0; - } -} -``` - -これで準備が整ったので、実行します。`IEnv` を実装したクラスのインスタンスをコンポーネントに渡すと、コンポーネントの内部から `ShowMessage()` や `ShowOptions()` が呼び出されます。 - -```cs -[SerializeField] private ComponentAsset componentAsset; - -var component = componentAsset.LoadComponent(); - -// コンポーネントのロード -var instance = component.Instantiate(null, new Dictionary() -{ - // `env`実装のインポート - { "my-game:my-sequencer/env", IEnv.CreateWaaSInstance(Env.Instance) } -}); - -using var context = new ExecutionContext(); -var wrapper = new ISequence.Wrapper(instance, context); - -// 実行 -await wrapper.Play(); -``` diff --git a/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/component-model.mdx b/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/component-model.mdx new file mode 100644 index 0000000..9f63f2c --- /dev/null +++ b/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/component-model.mdx @@ -0,0 +1,178 @@ +--- +title: Component Model ではじめる +sidebar_position: 3 +--- + +import rustImporterComponentSettingsImage from '@site/static/img/rust-importer-component-settings.png'; +import rustImporterImportedComponentImage from '@site/static/img/rust-importer-imported-component.png'; + +WaaS の **Component Model API** を使用すると、[WebAssembly Component Model](https://component-model.bytecodealliance.org/) に基づくコンポーネントを実行できます。 + +通常の WebAssembly モジュールでは引数や戻り値の型が数値に限られていましたが、Component Model では、文字列やリスト、構造体などより複雑な型を持つデータをやり取りすることができます。 + +このチュートリアルでは、Rust を使用してコンポーネントを作成し、Unity で実行する方法を説明します。 + +### 1. 必要なツールのインストール + +[Rust ツールチェイン](https://www.rust-lang.org/ja/learn/get-started)をインストールし、`wasm32-unknown-unknown`ターゲットを追加します。 + +```sh +rustup target add wasm32-unknown-unknown +``` + +### 2. WITファイルを作成する + +Component Model では、[**WIT**](https://component-model.bytecodealliance.org/design/wit.html) (WebAssembly Interface Type) という IDL を使って型や関数のシグネチャを事前に定義します。 + +まずは Unity プロジェクトのルート下に `wit/sequence.wit` を作成します。 +以下は簡単な会話シーンで使うことを想定した WIT の例です。 + +```wit +package my-game:my-sequencer; + +world sequence { + import env; + export play: func(); +} + +interface env { + show-message: func(speaker: string, message: string); +} +``` + +`world` はこの WebAssembly コンポーネントのインポート・エクスポートする機能のセットを定義するものです。`interface env` はホスト環境側で実装しコンポーネントにインポートする機能のセットです。 + +### 2. Rust ソースファイルを作成する + +Rust でコンポーネントを作成するには、上で作成した WIT ファイルから Rust 用のバインディングを生成する必要があります。これには [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) というツールを使います。使用する言語によって使い方は異なりますが、今回は Rust での使い方を紹介します。 + +`Assets` 下の任意の場所に `sequence_0.rs` を作成します。 + +```rust +use crate::my_game::my_sequencer::env::show_message; + +wit_bindgen::generate!({ + path: "../../../../../wit", + world: "my_game::my_sequencer/sequnce" +}); + +struct Sequence; + +impl Guest for Sequence { + fn play() { + show_message("ぼく", "こんにちは!"); + } +} + +export!(Sequence); +``` + +:::info +`wit-bindgen` WIT ファイルをもとに `generate!` マクロでバインディングを生成します。 +`wit-bindgen` は WIT を検索する際、Rust ソースファイルではなく `Library/com.ruccho.waas/rust/crates/~~~/Cargo.toml` を基準とします。`../../../../../wit` というパスは、この `Cargo.toml` からプロジェクトルートを参照するためのものです。 +詳しい背景については [Rust Importer の使用](../unity-integration/rust) を参照してください。 +::: + +### 3. 必要な設定を行ってインポートする + +作成した Rust ソースファイルを Unity プロジェクトにインポートします。 + +
+ +
+ +- **Crate Root** を有効化する +- **Dependencies** に **via Registry** な依存関係を追加し、Name に `wit-bindgen` を指定する +- **Componentize** を有効化する + - **Componentization Settings** で **Wit Directory** に `wit` を指定する + - **World** に `my-game::my-sequencer/sequnce` を指定する + +:::info +各インポート設定について詳しくは [Rust Importer の使用](../unity-integration/rust.mdx) を参照してください。 +::: + +**Apply** でインポートします。コンパイルとコンポーネント化が完了すると、インスペクタにコンポーネントの情報が表示されます。 + +
+ +
+ +関数 `play` がエクスポートされていることが確認できました。 + +### 4. C# 用のバインディングの生成 + +作成したコンポーネントを簡単に実行するために、C# のバインディングを作成しましょう。WaaS では `wit2waas` という CLI ツールを提供しており、WIT ファイルから C# のインターフェースを生成することができます。 + +Unity プロジェクトのルートで [`wit2waas`](../component-model/wit2waas) を実行します。 + +```sh +cargo install wit2waas +wit2waas --out "Assets/WaaS Generated" +``` + +すると、先ほどの WIT から `Assets/WaaS Generated/my-game/my-sequencer` に C# コードが生成されます。 + +```csharp +// +#nullable enable + +namespace MyGame.MySequencer +{ + // interface env + [global::WaaS.ComponentModel.Binding.ComponentInterface(@"env")] + public partial interface IEnv + { + [global::WaaS.ComponentModel.Binding.ComponentApi(@"show-message")] + global::System.Threading.Tasks.ValueTask ShowMessage(string @speaker, string @message); + + } +} +``` + +いろいろと属性がついていますが、これによって Source Generator が具体的に WebAssembly コンポーネントと値をやり取りするためのコードを生成します。 + +`IEnv` (WIT では `interface env`) は、C# から WebAssembly 側にインポートする機能のセットを定義したものでした。これに実装を与えておきます。 + +```csharp +internal class Env : IEnv +{ + public ValueTask ShowMessage(string speaker, string message) + { + Debug.Log($"{speaker}「{message}」"); + return default; + } +} +``` + +### 5. 実行する + +これで、Rust で作成したコンポーネントを Unity で実行する準備が整いました。 + +```csharp +using System.Collections.Generic; +using MyGame.MySequencer; +using UnityEngine; +using WaaS.ComponentModel.Runtime; +using WaaS.Runtime; +using WaaS.Unity; + +public class RunSequenceTest : MonoBehaviour +{ + [SerializeField] private ComponentAsset componentAsset; + + private async void Start() + { + var component = await componentAsset.LoadComponentAsync(); + var instance = component.Instantiate(new Dictionary() + { + { "my-game:my-sequencer/env", IEnv.CreateWaaSInstance(new Env()) } + }); + using var context = new ExecutionContext(); + var sequence = new ISequence.Wrapper(instance, context); + await sequence.Play(); // ぼく「こんにちは!」 + } +} +``` + +`RunSequenceTest` を GameObject にアタッチし、`Component Asset` に先ほどインポートしたコンポーネントをアサインします。 +実行すると、コンソールに `ぼく「こんにちは!」` と表示されるはずです。 \ No newline at end of file diff --git a/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/installation.md b/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/installation.mdx similarity index 100% rename from docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/installation.md rename to docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/installation.mdx diff --git a/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/unity-rust.md b/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/unity-rust.mdx similarity index 98% rename from docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/unity-rust.md rename to docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/unity-rust.mdx index 273a16b..180e029 100644 --- a/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/unity-rust.md +++ b/docs/i18n/ja/docusaurus-plugin-content-docs/current/getting-started/unity-rust.mdx @@ -13,7 +13,7 @@ WaaS では WebAssembly モジュールをインポートできますが、Rust rustup target add wasm32-unknown-unknown ``` -#### 2. Unity プロジェクトに [WaaS をインストール](./installation.md)します。 +#### 2. Unity プロジェクトに [WaaS をインストール](./installation.mdx)します。 #### 3. Unity プロジェクト内に `main.rs` を作成します: diff --git a/docs/i18n/ja/docusaurus-plugin-content-docs/current/unity-integration/index.mdx b/docs/i18n/ja/docusaurus-plugin-content-docs/current/unity-integration/index.mdx new file mode 100644 index 0000000..003ebc5 --- /dev/null +++ b/docs/i18n/ja/docusaurus-plugin-content-docs/current/unity-integration/index.mdx @@ -0,0 +1,9 @@ +--- +title: Unity との統合 +sidebar_position: 5 +--- +import DocCardList from '@theme/DocCardList'; + +各ゲスト言語向けに、Unity でのワークフローを改善する機能を提供しています。対応言語追加の要望があれば [Issues](https://github.com/ruccho/WaaS/issues) を探すか、新たに作成してください。 + + \ No newline at end of file diff --git a/docs/i18n/ja/docusaurus-plugin-content-docs/current/unity-integration/rust.mdx b/docs/i18n/ja/docusaurus-plugin-content-docs/current/unity-integration/rust.mdx new file mode 100644 index 0000000..c2e35b2 --- /dev/null +++ b/docs/i18n/ja/docusaurus-plugin-content-docs/current/unity-integration/rust.mdx @@ -0,0 +1,122 @@ +--- +title: Rust Importer の使用 +sidebar_position: 1 +--- + +import Badge from '@site/src/components/Badge'; +import rustImporterImage from '@site/static/img/rust-importer.png'; + +# Rust Importer の使用 + +WaaS では、`*.rs` ファイルをアセットとしてインポートすることで直接 WebAssembly にコンパイルすることができます。 + +:::info +Rust Importer を利用するには [Rust ツールチェイン](https://www.rust-lang.org/ja/tools/install) と [`wasm32-unknown-unknown`](https://doc.rust-lang.org/rustc/platform-support/wasm32-unknown-unknown.html#building-rust-programs) ターゲットが必要です。 +::: + +## インポート設定 + +
+ +
+ +- **Create Root**: ソースファイルをクレートのルートとして扱うかを指定します。チェックを外すと、ソースファイルは直接 WebAssembly にコンパイルされませんが、ほかのソースファイルからモジュールとして参照できます。 +- **Dependencies**: 依存するパッケージを指定します。 + - **via Registry**: パッケージレジストリからパッケージをインストールします。 + - **via Git**: Git リポジトリからパッケージをインストールします。 +- **Componentize**: WebAssembly モジュールをコンポーネントに変換します。詳しくは [コンポーネント化](#コンポーネント化) を参照してください。 + +### Dependency + +- **Name**: パッケージ名を指定します。 +- **Version Requirements**: バージョンを指定します。詳しくは [The Cargo Book](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html) を参照してください。 +- **Features**: 有効化する feature を指定します。 + +#### via Registry 固有の項目 + +- **Registry** (optional): パッケージを取得するレジストリを指定します。詳しくは [The Cargo Book](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-other-registries) を参照してください。 + +#### via Git 固有の項目 + +- **Git Url**: Git リポジトリの URL を指定します。 +- **Branch** (optional): ブランチ名を指定します。 +- **Tag** (optional): タグ名またはコミットハッシュを指定します。 +- **Rev** (optional): コミットハッシュを指定します。 + + +:::warning +Rust Importer はソースファイルの変更を検知して自動的に再コンパイルを行います。ただし、次の場合は変更が検知されません。 + +- 依存先のソースファイルが Unity の管理外にある場合 +- 依存先のソースファイルが存在しないためコンパイルエラーになった後、該当するソースファイルが作成・変更された場合 +- `.rs` 以外のファイルに依存している場合 + +これらの場合は、対象のソースファイルを右クリックして `Reimport` を行うことで再コンパイルされます。 +また、`Assets` > `Recompile Rust scripts for WaaS` を選択すると、プロジェクト内のすべての Rust ソースファイルを再コンパイルします。 +::: + +### プリセットの利用 + +上記の設定を**プリセット アセット**として保存することで再利用できます。 + +`Create` > `WaaS` > `Rust Importer Preset` からプリセットを作成し、Rust Importer の Inspector の **Preset** 欄にセットしてください。 + +## IDE 機能の利用 + +Rust に対応する IDE で **Unity プロジェクトのルート**をワークスペースとして開くと、IDE による補完等の機能を利用しながら Rust ソースファイルを編集できます。 + +これは、Rust Importer が Unity プロジェクトのルートに `Cargo.toml` を作成することによって実現しています: + +```toml +[workspace] +members = ["Library/com.ruccho.waas/rust/crates/*"] # Used by WaaS +resolver = "2" # Used by WaaS + +``` + +内部的には `Library/com.ruccho.waas/rust/crates/*` に **Create Root** として指定された各 `*.rs` をルートとするクレート群が自動生成されます。これらクレートの `Cargo.toml` は Rust Importer が自動的に上書きするので、手動での編集はできません。 + +## Rust Importer の無効化 + +Define Symbol として `WAAS_DISABLE_RUST_IMPORTER` を指定すると、Rust Importer が無効化されます。 + +## コンポーネント化 + +:::info +コンポーネント化については [Component Model ではじめる](../getting-started/component-model) を参照してください。 +::: + +## ベストプラクティス + +### バージョン管理 + +Unity プロジェクトのルートに作成された `Cargo.toml` および `Cargo.lock` をバージョン管理に追加することをお勧めします。 + +### wee_alloc の使用 + +[wee_alloc](https://github.com/rustwasm/wee_alloc) は、コードサイズの削減を目的とした WebAssembly 向けのアロケータです。 + +依存関係に `wee_alloc` を追加し、次のコードを追加します。 + +```rust +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; +``` + +### ビルド設定 + +Unity プロジェクトのルートに生成された `Cargo.toml` を編集して、コードサイズ最適化のためのオプションを追加できます。 + +```toml +## ... + +[profile.release] +lto = true +codegen-units = 1 +opt-level = "z" +``` + +:::info +現在、Rust Importer は常に `--release` プロファイルでビルドします。 +::: + diff --git a/docs/static/img/rust-importer-component-settings.png b/docs/static/img/rust-importer-component-settings.png new file mode 100644 index 0000000..807dc15 Binary files /dev/null and b/docs/static/img/rust-importer-component-settings.png differ diff --git a/docs/static/img/rust-importer-imported-component.png b/docs/static/img/rust-importer-imported-component.png new file mode 100644 index 0000000..a4603d4 Binary files /dev/null and b/docs/static/img/rust-importer-imported-component.png differ diff --git a/docs/static/img/rust-importer.png b/docs/static/img/rust-importer.png new file mode 100644 index 0000000..d6607a5 Binary files /dev/null and b/docs/static/img/rust-importer.png differ