create a 'common-api' crate to make it easier to work with the ecs world

This commit is contained in:
SeanOMik 2024-04-14 19:34:07 -04:00
parent af1ec73fb5
commit 27c8f93611
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
12 changed files with 462 additions and 432 deletions

175
Cargo.lock generated
View File

@ -136,6 +136,26 @@ version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytemuck"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.5.0" version = "1.5.0"
@ -241,6 +261,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "common-api"
version = "0.1.0"
dependencies = [
"bytemuck",
"thiserror",
"wit-bindgen",
]
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.6" version = "0.8.6"
@ -370,7 +399,7 @@ dependencies = [
"itertools", "itertools",
"log", "log",
"smallvec", "smallvec",
"wasmparser", "wasmparser 0.201.0",
"wasmtime-types", "wasmtime-types",
] ]
@ -690,6 +719,9 @@ name = "heck"
version = "0.4.1" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
@ -1297,6 +1329,15 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "spdx"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ef1a0fa1e39ac22972c8db23ff89aea700ab96aa87114e1fb55937a631a0c9"
dependencies = [
"smallvec",
]
[[package]] [[package]]
name = "sptr" name = "sptr"
version = "0.3.2" version = "0.3.2"
@ -1499,6 +1540,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.11" version = "0.1.11"
@ -1612,6 +1659,22 @@ dependencies = [
"leb128", "leb128",
] ]
[[package]]
name = "wasm-metadata"
version = "0.202.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "094aea3cb90e09f16ee25a4c0e324b3e8c934e7fd838bfa039aef5352f44a917"
dependencies = [
"anyhow",
"indexmap",
"serde",
"serde_derive",
"serde_json",
"spdx",
"wasm-encoder 0.202.0",
"wasmparser 0.202.0",
]
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.201.0" version = "0.201.0"
@ -1623,6 +1686,17 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "wasmparser"
version = "0.202.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6998515d3cf3f8b980ef7c11b29a9b1017d4cf86b99ae93b546992df9931413"
dependencies = [
"bitflags 2.5.0",
"indexmap",
"semver",
]
[[package]] [[package]]
name = "wasmprinter" name = "wasmprinter"
version = "0.201.0" version = "0.201.0"
@ -1630,7 +1704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a67e66da702706ba08729a78e3c0079085f6bfcb1a62e4799e97bbf728c2c265" checksum = "a67e66da702706ba08729a78e3c0079085f6bfcb1a62e4799e97bbf728c2c265"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"wasmparser", "wasmparser 0.201.0",
] ]
[[package]] [[package]]
@ -1663,7 +1737,7 @@ dependencies = [
"serde_json", "serde_json",
"target-lexicon", "target-lexicon",
"wasm-encoder 0.201.0", "wasm-encoder 0.201.0",
"wasmparser", "wasmparser 0.201.0",
"wasmtime-cache", "wasmtime-cache",
"wasmtime-component-macro", "wasmtime-component-macro",
"wasmtime-component-util", "wasmtime-component-util",
@ -1720,7 +1794,7 @@ dependencies = [
"syn", "syn",
"wasmtime-component-util", "wasmtime-component-util",
"wasmtime-wit-bindgen", "wasmtime-wit-bindgen",
"wit-parser", "wit-parser 0.201.0",
] ]
[[package]] [[package]]
@ -1748,7 +1822,7 @@ dependencies = [
"object", "object",
"target-lexicon", "target-lexicon",
"thiserror", "thiserror",
"wasmparser", "wasmparser 0.201.0",
"wasmtime-cranelift-shared", "wasmtime-cranelift-shared",
"wasmtime-environ", "wasmtime-environ",
"wasmtime-versioned-export-macros", "wasmtime-versioned-export-macros",
@ -1790,7 +1864,7 @@ dependencies = [
"target-lexicon", "target-lexicon",
"thiserror", "thiserror",
"wasm-encoder 0.201.0", "wasm-encoder 0.201.0",
"wasmparser", "wasmparser 0.201.0",
"wasmprinter", "wasmprinter",
"wasmtime-component-util", "wasmtime-component-util",
"wasmtime-types", "wasmtime-types",
@ -1880,7 +1954,7 @@ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
"thiserror", "thiserror",
"wasmparser", "wasmparser 0.201.0",
] ]
[[package]] [[package]]
@ -1936,7 +2010,7 @@ dependencies = [
"gimli", "gimli",
"object", "object",
"target-lexicon", "target-lexicon",
"wasmparser", "wasmparser 0.201.0",
"wasmtime-cranelift-shared", "wasmtime-cranelift-shared",
"wasmtime-environ", "wasmtime-environ",
"winch-codegen", "winch-codegen",
@ -1951,7 +2025,7 @@ dependencies = [
"anyhow", "anyhow",
"heck", "heck",
"indexmap", "indexmap",
"wit-parser", "wit-parser 0.201.0",
] ]
[[package]] [[package]]
@ -2067,7 +2141,7 @@ dependencies = [
"regalloc2", "regalloc2",
"smallvec", "smallvec",
"target-lexicon", "target-lexicon",
"wasmparser", "wasmparser 0.201.0",
"wasmtime-environ", "wasmtime-environ",
] ]
@ -2238,6 +2312,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb4e7653763780be47e38f479e9aa83c768aa6a3b2ed086dc2826fdbbb7e7f5" checksum = "9fb4e7653763780be47e38f479e9aa83c768aa6a3b2ed086dc2826fdbbb7e7f5"
dependencies = [ dependencies = [
"wit-bindgen-rt", "wit-bindgen-rt",
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b67e11c950041849a10828c7600ea62a4077c01e8af72e8593253575428f91b"
dependencies = [
"anyhow",
"wit-parser 0.202.0",
] ]
[[package]] [[package]]
@ -2249,6 +2334,53 @@ dependencies = [
"bitflags 2.5.0", "bitflags 2.5.0",
] ]
[[package]]
name = "wit-bindgen-rust"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30acbe8fb708c3a830a33c4cb705df82659bf831b492ec6ca1a17a369cfeeafb"
dependencies = [
"anyhow",
"heck",
"indexmap",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b1b06eae85feaecdf9f2854f7cac124e00d5a6e5014bfb02eb1ecdeb5f265b9"
dependencies = [
"anyhow",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.202.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c836b1fd9932de0431c1758d8be08212071b6bba0151f7bac826dbc4312a2a9"
dependencies = [
"anyhow",
"bitflags 2.5.0",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder 0.202.0",
"wasm-metadata",
"wasmparser 0.202.0",
"wit-parser 0.202.0",
]
[[package]] [[package]]
name = "wit-parser" name = "wit-parser"
version = "0.201.0" version = "0.201.0"
@ -2264,13 +2396,33 @@ dependencies = [
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"unicode-xid", "unicode-xid",
"wasmparser", "wasmparser 0.201.0",
]
[[package]]
name = "wit-parser"
version = "0.202.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744237b488352f4f27bca05a10acb79474415951c450e52ebd0da784c1df2bcc"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser 0.202.0",
] ]
[[package]] [[package]]
name = "witguest" name = "witguest"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bytemuck",
"common-api",
"wit-bindgen", "wit-bindgen",
] ]
@ -2279,6 +2431,7 @@ name = "wittest"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"common-api",
"lyra-ecs", "lyra-ecs",
"slab", "slab",
"thiserror", "thiserror",

View File

@ -1,4 +1,4 @@
workspace = { members = ["witguest"] } workspace = { members = [ "common-api","witguest"] }
[package] [package]
name = "wittest" name = "wittest"
version = "0.1.0" version = "0.1.0"
@ -14,3 +14,5 @@ wasmtime-wasi = "19.0.2"
lyra-ecs = { path = "./lyra-engine/lyra-ecs" } lyra-ecs = { path = "./lyra-engine/lyra-ecs" }
slab = "0.4.9" slab = "0.4.9"
thiserror = "1.0.58" thiserror = "1.0.58"
common-api = { path = "./common-api" }

14
common-api/Cargo.toml Executable file
View File

@ -0,0 +1,14 @@
[package]
name = "common-api"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bytemuck = "1.15.0"
thiserror = "1.0.58"
#bytemuck = "1.15.0"
#wasmtime = "19.0.2"
wit-bindgen = "0.24.0"
#wit-bindgen = { version = "0.24.0", default-features = false, features = ["realloc"] }

162
common-api/src/lib.rs Executable file
View File

@ -0,0 +1,162 @@
/* pub mod bindings {
wasmtime::component::bindgen!({
world: "example",
path: "../witguest/wit/world.wit",
//tracing: true,
async: true,
});
}
use bindings::{component::witguest::ecs::EcsWorld, *}; */
use std::{alloc::Layout, any::TypeId, marker::PhantomData, mem};
pub use bytemuck;
wit_bindgen::generate!({
world: "api",
path: "wit",
});
use lyra::api::ecs::{ComponentInfo, EcsDynamicView, EcsWorld, Entity, WasmTypeId};
impl WasmTypeId {
pub fn of<T: 'static>() -> Self {
Self {
inner: unsafe { mem::transmute(TypeId::of::<T>()) }
}
}
}
impl ComponentInfo {
pub fn of<C: Component>() -> Self {
let layout = Layout::new::<C>();
Self {
size: layout.size() as _,
alignment: layout.align() as _,
type_id: WasmTypeId::of::<C>(),
}
}
}
#[derive(Debug)]
pub enum ComponentSerializationMethod {
/// Serializes the component into bytes using [`bytemuck::bytes_of`].
Bytemuck,
/// Serializes the component into bytes using
/// [bincode](https://github.com/bincode-org/bincode).
Bincode
}
#[derive(Debug, thiserror::Error)]
pub enum ComponentSerializationError {
#[error("{0}")]
Bytemuck(bytemuck::PodCastError),
}
pub trait Component: Sized + 'static {
const SERIALIZATION: ComponentSerializationMethod;
fn component_info() -> ComponentInfo;
fn to_bytes(&self) -> Result<Vec<u8>, ComponentSerializationError>;
fn to_bytes_into(&self, target: &mut Vec<u8>) -> Result<(), ComponentSerializationError>;
fn from_bytes(bytes: &[u8]) -> Result<Self, ComponentSerializationError>;
}
#[macro_export]
macro_rules! bytemuck_component_impl {
($type:ident) => {
impl common_api::Component for $type {
const SERIALIZATION: common_api::ComponentSerializationMethod = common_api::ComponentSerializationMethod::Bytemuck;
fn component_info() -> common_api::lyra::api::ecs::ComponentInfo {
common_api::lyra::api::ecs::ComponentInfo::of::<$type>()
}
fn to_bytes(&self) -> Result<Vec<u8>, common_api::ComponentSerializationError> {
Ok(common_api::bytemuck::bytes_of(self).to_vec())
}
fn to_bytes_into(&self, target: &mut Vec<u8>) -> Result<(), common_api::ComponentSerializationError> {
let bytes = self.to_bytes()?;
target.extend(bytes.into_iter());
Ok(())
}
fn from_bytes(bytes: &[u8]) -> Result<Self, common_api::ComponentSerializationError> {
common_api::bytemuck::try_from_bytes::<Self>(&bytes)
.map_err(|e| common_api::ComponentSerializationError::Bytemuck(e))
.cloned()
}
}
};
}
#[derive(Default)]
pub struct ComponentBundle {
bundle: Vec<u8>,
infos: Vec<ComponentInfo>,
}
impl ComponentBundle {
pub fn new() -> Self {
Self::default()
}
pub fn push<C: Component>(&mut self, c: C) {
let info = C::component_info();
self.infos.push(info);
c.to_bytes_into(&mut self.bundle);
/* self.bundle.reserve(info.size as _);
self.bundle.extend(comp_bytes.into_iter()); */
// dont call the destructor on C to avo
//mem::forget(c);
}
}
pub struct World {
inner: EcsWorld,
}
impl World {
pub fn new() -> Self {
Self {
inner: EcsWorld::new(),
}
}
pub fn spawn(&self, components: ComponentBundle) -> Entity {
self.inner.spawn(&components.bundle, &components.infos)
}
pub fn view(&self, components: &[ComponentInfo]) -> View {
View::from(self.inner.view(components))
}
}
pub struct View {
inner: EcsDynamicView,
}
impl From<EcsDynamicView> for View {
fn from(value: EcsDynamicView) -> Self {
Self {
inner: value
}
}
}
impl View {
pub fn next<C: Component>(&mut self) -> Option<C> {
let row = self.inner.next();
if let Some(row) = row {
Some(C::from_bytes(&row).unwrap())
} else {
None
}
}
}

36
common-api/wit/ecs.wit Normal file
View File

@ -0,0 +1,36 @@
package lyra:api;
interface ecs {
record entity-id {
id: u64,
}
record entity {
id: entity-id,
generation: u64,
}
record wasm-type-id {
inner: tuple<u64, u64>,
}
record component-info {
size: u64,
alignment: u64,
type-id: wasm-type-id, // a u128
}
resource ecs-dynamic-view {
constructor(wrld: borrow<ecs-world>, component-infos: list<component-info>);
next: func() -> option<list<u8>>;
}
resource ecs-world {
constructor();
// expects components to be tightly packed in the same order of component-infos
spawn: func(components: list<u8>, component-infos: list<component-info>) -> entity;
view: func(component-infos: list<component-info>) -> ecs-dynamic-view;
}
}

5
common-api/wit/world.wit Normal file
View File

@ -0,0 +1,5 @@
package lyra:api;
world api {
import ecs;
}

View File

@ -22,6 +22,7 @@ pkgs.mkShell.override {
rust rust
] ++ (with pkgs; [ ] ++ (with pkgs; [
cargo-component wasmtime wasm-tools cargo-component wasmtime wasm-tools
cargo-expand
]); ]);
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs; LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
} }

View File

@ -1,11 +1,8 @@
use std::alloc::Layout; use std::alloc::Layout;
use std::any::TypeId;
use std::mem;
use std::ptr::NonNull; use std::ptr::NonNull;
use async_trait::async_trait; use async_trait::async_trait;
use component::witguest::ecs::WasmTypeId; use component::witguest::math;
use component::witguest::{ecs as wasm_ecs, math};
use ecs::query::dynamic::{DynamicViewState, DynamicViewStateIter, QueryDynamicType}; use ecs::query::dynamic::{DynamicViewState, DynamicViewStateIter, QueryDynamicType};
use lyra_ecs::Component; use lyra_ecs::Component;
@ -13,7 +10,7 @@ use lyra_ecs as ecs;
use slab::Slab; use slab::Slab;
use thiserror::Error; use thiserror::Error;
use wasmtime_wasi::WasiView; use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiView};
use wasmtime::component::Resource as WasmResource; use wasmtime::component::Resource as WasmResource;
@ -22,35 +19,18 @@ pub(crate) mod lyra_engine {
pub use lyra_ecs as ecs; pub use lyra_ecs as ecs;
} }
wasmtime::component::bindgen!({ use lyra::api::ecs as wasm_ecs;
world: "example",
path: "witguest/wit/world.wit",
//tracing: true,
async: true,
});
impl WasmTypeId { impl Into<ecs::DynTypeId> for wasm_ecs::WasmTypeId {
pub fn of<T: 'static>() -> Self {
Self {
inner: unsafe { mem::transmute(TypeId::of::<T>()) },
}
}
}
impl Into<ecs::DynTypeId> for WasmTypeId {
fn into(self) -> ecs::DynTypeId { fn into(self) -> ecs::DynTypeId {
// SAFETY: the memory layout of (u64, u64) is the same as u128 ecs::DynTypeId::Unknown(unsafe { std::mem::transmute(self.inner) })
ecs::DynTypeId::Unknown(unsafe { mem::transmute(self.inner) })
} }
} }
impl Into<ecs::ComponentInfo> for wasm_ecs::ComponentInfo { impl Into<ecs::ComponentInfo> for wasm_ecs::ComponentInfo {
fn into(self) -> ecs::ComponentInfo { fn into(self) -> ecs::ComponentInfo {
ecs::ComponentInfo::new_unknown( let layout = Layout::from_size_align(self.size as _, self.alignment as _).unwrap();
None, ecs::ComponentInfo::new_unknown(None, self.type_id, layout)
self.type_id,
Layout::from_size_align(self.size as _, self.alignment as _).unwrap(),
)
} }
} }
@ -63,6 +43,31 @@ impl From<ecs::Entity> for wasm_ecs::Entity {
} }
} }
impl Into<ecs::Entity> for wasm_ecs::Entity {
fn into(self) -> ecs::Entity {
let mut id = ecs::EntityId(self.id.id);
ecs::Entity::new(id, self.generation)
}
}
wasmtime::component::bindgen!({
world: "api",
path: "common-api/wit",
//tracing: true,
async: true,
});
wasmtime::component::bindgen!({
world: "example",
path: "witguest/wit",
//tracing: true,
async: true,
with: {
"lyra:api": lyra::api,
}
});
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Component)] #[derive(Debug, Clone, Copy, PartialEq, Component)]
struct Vec3 { struct Vec3 {
@ -88,15 +93,18 @@ unsafe impl Send for DynamicViewEntry {}
struct Imports { struct Imports {
world_slab: Slab<WorldEntry>, world_slab: Slab<WorldEntry>,
world_views_slab: Slab<DynamicViewEntry>, world_views_slab: Slab<DynamicViewEntry>,
ctx: WasiCtx,
table: ResourceTable,
} }
impl WasiView for Imports { impl WasiView for Imports {
fn table(&mut self) -> &mut wasmtime_wasi::ResourceTable { fn table(&mut self) -> &mut wasmtime_wasi::ResourceTable {
todo!() &mut self.table
} }
fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx { fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx {
todo!() &mut self.ctx
} }
} }
@ -108,8 +116,6 @@ impl ExampleImports for Imports {
} }
} }
impl math::Host for Imports {}
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum WasmError { pub enum WasmError {
#[error("an invalid '{0}' resource handle was used by the guest")] #[error("an invalid '{0}' resource handle was used by the guest")]
@ -181,6 +187,8 @@ impl wasm_ecs::HostEcsWorld for Imports {
} }
} }
impl math::Host for Imports {}
#[async_trait] #[async_trait]
impl wasm_ecs::HostEcsDynamicView for Imports { impl wasm_ecs::HostEcsDynamicView for Imports {
async fn new( async fn new(
@ -284,7 +292,10 @@ async fn main() -> wasmtime::Result<()> {
let mut linker = wasmtime::component::Linker::new(&engine); let mut linker = wasmtime::component::Linker::new(&engine);
wasmtime_wasi::bindings::Imports::add_to_linker(&mut linker, |s| s)?; wasmtime_wasi::bindings::Imports::add_to_linker(&mut linker, |s| s)?;
Example::add_to_linker(&mut linker, |s| s)?; Example::add_to_linker(&mut linker, |s| s)?;
Api::add_to_linker(&mut linker, |s| s)?;
let mut builder = WasiCtxBuilder::new();
builder.inherit_stdio();
// memory per instance // memory per instance
let mut store = wasmtime::Store::new( let mut store = wasmtime::Store::new(
&engine, &engine,
@ -293,6 +304,8 @@ async fn main() -> wasmtime::Result<()> {
// wants another world for some reason. // wants another world for some reason.
world_slab: Slab::with_capacity(1), world_slab: Slab::with_capacity(1),
world_views_slab: Slab::with_capacity(10), world_views_slab: Slab::with_capacity(10),
ctx: builder.build(),
table: ResourceTable::new(),
}, },
); );
@ -303,8 +316,8 @@ async fn main() -> wasmtime::Result<()> {
// Instantiate the component // Instantiate the component
println!("creating example"); println!("creating example");
let (example, _instance) = Example::instantiate_async(&mut store, &component, &linker).await?; let (example, _instance) = Example::instantiate_async(&mut store, &component, &linker).await?;
let result = example.call_on_init(&mut store).await?.unwrap(); example.call_on_init(&mut store).await?.unwrap();
println!("Guest returned: {}", result); println!("Guest is done");
Ok(()) Ok(())
} }

View File

@ -7,6 +7,9 @@ edition = "2021"
[dependencies] [dependencies]
wit-bindgen = { version = "0.24.0", default-features = false, features = ["realloc"] } wit-bindgen = { version = "0.24.0", default-features = false, features = ["realloc"] }
common-api = { path = "../common-api" }
bytemuck = { version = "1.15.0", features = ["derive"] }
#bytemuck = { version = "1.15.0", features = ["derive"] }
[lib] [lib]
crate-type = ["cdylib"] crate-type = ["cdylib"]
@ -15,3 +18,6 @@ crate-type = ["cdylib"]
package = "component:witguest" package = "component:witguest"
[package.metadata.component.dependencies] [package.metadata.component.dependencies]
[package.metadata.component.target.dependencies]
#"lyra:api" = { path = "../common-api/wit" }

View File

@ -45,29 +45,16 @@ const _: () = {
wit_bindgen::rt::run_ctors_once(); wit_bindgen::rt::run_ctors_once();
let result0 = <_GuestImpl as Guest>::on_init(); let result0 = <_GuestImpl as Guest>::on_init();
let ptr1 = _RET_AREA.0.as_mut_ptr() as i32; let result1 = match result0 {
match result0 { Ok(_) => { 0i32 },
Ok(e) => { { Err(_) => { 1i32 },
*((ptr1 + 0) as *mut u8) = (0i32) as u8; };result1
*((ptr1 + 1) as *mut u8) = (wit_bindgen::rt::as_i32(e)) as u8;
} },
Err(_) => { {
*((ptr1 + 0) as *mut u8) = (1i32) as u8;
} },
};ptr1
} }
}; };
use super::Component as _GuestImpl; use super::Component as _GuestImpl;
pub trait Guest { pub trait Guest {
fn on_init() -> Result<u8,()>; fn on_init() -> Result<(),()>;
} }
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
#[repr(align(1))]
struct _RetArea([u8; 2]);
static mut _RET_AREA: _RetArea = _RetArea([0; 2]);
pub mod component { pub mod component {
pub mod witguest { pub mod witguest {
@ -80,301 +67,13 @@ pub mod component {
} }
#[allow(clippy::all)]
pub mod ecs {
#[used]
#[doc(hidden)]
#[cfg(target_arch = "wasm32")]
static __FORCE_SECTION_REF: fn() = super::super::super::__link_section;
/// use math.{vec3};
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EntityId {
pub id: u64,
}
impl ::core::fmt::Debug for EntityId {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct("EntityId").field("id", &self.id).finish()
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Entity {
pub id: EntityId,
pub generation: u64,
}
impl ::core::fmt::Debug for Entity {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct("Entity").field("id", &self.id).field("generation", &self.generation).finish()
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct WasmTypeId {
pub inner: (u64,u64,),
}
impl ::core::fmt::Debug for WasmTypeId {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct("WasmTypeId").field("inner", &self.inner).finish()
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ComponentInfo {
pub size: u64,
pub alignment: u64,
pub type_id: WasmTypeId,
}
impl ::core::fmt::Debug for ComponentInfo {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct("ComponentInfo").field("size", &self.size).field("alignment", &self.alignment).field("type-id", &self.type_id).finish()
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct EcsDynamicView{
handle: wit_bindgen::rt::Resource<EcsDynamicView>,
}
impl EcsDynamicView{
#[doc(hidden)]
pub unsafe fn from_handle(handle: u32) -> Self {
Self {
handle: wit_bindgen::rt::Resource::from_handle(handle),
}
}
#[doc(hidden)]
pub fn handle(&self) -> u32 {
wit_bindgen::rt::Resource::handle(&self.handle)
}
}
unsafe impl wit_bindgen::rt::WasmResource for EcsDynamicView{
#[inline]
unsafe fn drop(_handle: u32) {
#[cfg(not(target_arch = "wasm32"))]
unreachable!();
#[cfg(target_arch = "wasm32")]
{
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[resource-drop]ecs-dynamic-view"]
fn drop(_: u32);
}
drop(_handle);
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct EcsWorld{
handle: wit_bindgen::rt::Resource<EcsWorld>,
}
impl EcsWorld{
#[doc(hidden)]
pub unsafe fn from_handle(handle: u32) -> Self {
Self {
handle: wit_bindgen::rt::Resource::from_handle(handle),
}
}
#[doc(hidden)]
pub fn handle(&self) -> u32 {
wit_bindgen::rt::Resource::handle(&self.handle)
}
}
unsafe impl wit_bindgen::rt::WasmResource for EcsWorld{
#[inline]
unsafe fn drop(_handle: u32) {
#[cfg(not(target_arch = "wasm32"))]
unreachable!();
#[cfg(target_arch = "wasm32")]
{
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[resource-drop]ecs-world"]
fn drop(_: u32);
}
drop(_handle);
}
}
}
impl EcsDynamicView {
#[allow(unused_unsafe, clippy::all)]
pub fn new(wrld: &EcsWorld,component_infos: &[ComponentInfo],) -> Self{
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
unsafe {
let vec0 = component_infos;
let ptr0 = vec0.as_ptr() as i32;
let len0 = vec0.len() as i32;
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[constructor]ecs-dynamic-view"]
fn wit_import(_: i32, _: i32, _: i32, ) -> i32;
}
#[cfg(not(target_arch = "wasm32"))]
fn wit_import(_: i32, _: i32, _: i32, ) -> i32{ unreachable!() }
let ret = wit_import((wrld).handle() as i32, ptr0, len0);
EcsDynamicView::from_handle(ret as u32)
}
}
}
impl EcsDynamicView {
#[allow(unused_unsafe, clippy::all)]
pub fn next(&self,) -> Option<wit_bindgen::rt::vec::Vec::<u8>>{
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
unsafe {
#[repr(align(4))]
struct RetArea([u8; 12]);
let mut ret_area = ::core::mem::MaybeUninit::<RetArea>::uninit();
let ptr0 = ret_area.as_mut_ptr() as i32;
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[method]ecs-dynamic-view.next"]
fn wit_import(_: i32, _: i32, );
}
#[cfg(not(target_arch = "wasm32"))]
fn wit_import(_: i32, _: i32, ){ unreachable!() }
wit_import((self).handle() as i32, ptr0);
let l1 = i32::from(*((ptr0 + 0) as *const u8));
match l1 {
0 => None,
1 => {
let e = {
let l2 = *((ptr0 + 4) as *const i32);
let l3 = *((ptr0 + 8) as *const i32);
let len4 = l3 as usize;
Vec::from_raw_parts(l2 as *mut _, len4, len4)
};
Some(e)
}
_ => wit_bindgen::rt::invalid_enum_discriminant(),
}
}
}
}
impl EcsWorld {
#[allow(unused_unsafe, clippy::all)]
pub fn new() -> Self{
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
unsafe {
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[constructor]ecs-world"]
fn wit_import() -> i32;
}
#[cfg(not(target_arch = "wasm32"))]
fn wit_import() -> i32{ unreachable!() }
let ret = wit_import();
EcsWorld::from_handle(ret as u32)
}
}
}
impl EcsWorld {
#[allow(unused_unsafe, clippy::all)]
/// expects components to be tightly packed in the same order of component-infos
pub fn spawn(&self,components: &[u8],component_infos: &[ComponentInfo],) -> Entity{
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
unsafe {
#[repr(align(8))]
struct RetArea([u8; 16]);
let mut ret_area = ::core::mem::MaybeUninit::<RetArea>::uninit();
let vec0 = components;
let ptr0 = vec0.as_ptr() as i32;
let len0 = vec0.len() as i32;
let vec1 = component_infos;
let ptr1 = vec1.as_ptr() as i32;
let len1 = vec1.len() as i32;
let ptr2 = ret_area.as_mut_ptr() as i32;
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[method]ecs-world.spawn"]
fn wit_import(_: i32, _: i32, _: i32, _: i32, _: i32, _: i32, );
}
#[cfg(not(target_arch = "wasm32"))]
fn wit_import(_: i32, _: i32, _: i32, _: i32, _: i32, _: i32, ){ unreachable!() }
wit_import((self).handle() as i32, ptr0, len0, ptr1, len1, ptr2);
let l3 = *((ptr2 + 0) as *const i64);
let l4 = *((ptr2 + 8) as *const i64);
Entity{
id: EntityId{
id: l3 as u64,
},
generation: l4 as u64,
}
}
}
}
impl EcsWorld {
#[allow(unused_unsafe, clippy::all)]
pub fn view(&self,component_infos: &[ComponentInfo],) -> EcsDynamicView{
#[allow(unused_imports)]
use wit_bindgen::rt::{alloc, vec::Vec, string::String};
unsafe {
let vec0 = component_infos;
let ptr0 = vec0.as_ptr() as i32;
let len0 = vec0.len() as i32;
#[cfg(target_arch = "wasm32")]
#[link(wasm_import_module = "component:witguest/ecs")]
extern "C" {
#[link_name = "[method]ecs-world.view"]
fn wit_import(_: i32, _: i32, _: i32, ) -> i32;
}
#[cfg(not(target_arch = "wasm32"))]
fn wit_import(_: i32, _: i32, _: i32, ) -> i32{ unreachable!() }
let ret = wit_import((self).handle() as i32, ptr0, len0);
EcsDynamicView::from_handle(ret as u32)
}
}
}
}
} }
} }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[link_section = "component-type:example"] #[link_section = "component-type:example"]
#[doc(hidden)] #[doc(hidden)]
pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 745] = [0, 97, 115, 109, 13, 0, 1, 0, 0, 25, 22, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 101, 110, 99, 111, 100, 105, 110, 103, 4, 0, 7, 236, 4, 1, 65, 2, 1, 65, 9, 1, 66, 2, 1, 114, 3, 1, 120, 118, 1, 121, 118, 1, 122, 118, 4, 0, 4, 118, 101, 99, 51, 3, 0, 0, 3, 1, 23, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 109, 97, 116, 104, 5, 0, 1, 66, 28, 1, 114, 1, 2, 105, 100, 119, 4, 0, 9, 101, 110, 116, 105, 116, 121, 45, 105, 100, 3, 0, 0, 1, 114, 2, 2, 105, 100, 1, 10, 103, 101, 110, 101, 114, 97, 116, 105, 111, 110, 119, 4, 0, 6, 101, 110, 116, 105, 116, 121, 3, 0, 2, 1, 111, 2, 119, 119, 1, 114, 1, 5, 105, 110, 110, 101, 114, 4, 4, 0, 12, 119, 97, 115, 109, 45, 116, 121, 112, 101, 45, 105, 100, 3, 0, 5, 1, 114, 3, 4, 115, 105, 122, 101, 119, 9, 97, 108, 105, 103, 110, 109, 101, 110, 116, 119, 7, 116, 121, 112, 101, 45, 105, 100, 6, 4, 0, 14, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 105, 110, 102, 111, 3, 0, 7, 4, 0, 16, 101, 99, 115, 45, 100, 121, 110, 97, 109, 105, 99, 45, 118, 105, 101, 119, 3, 1, 4, 0, 9, 101, 99, 115, 45, 119, 111, 114, 108, 100, 3, 1, 1, 104, 10, 1, 112, 8, 1, 105, 9, 1, 64, 2, 4, 119, 114, 108, 100, 11, 15, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 105, 110, 102, 111, 115, 12, 0, 13, 4, 0, 29, 91, 99, 111, 110, 115, 116, 114, 117, 99, 116, 111, 114, 93, 101, 99, 115, 45, 100, 121, 110, 97, 109, 105, 99, 45, 118, 105, 101, 119, 1, 14, 1, 104, 9, 1, 112, 125, 1, 107, 16, 1, 64, 1, 4, 115, 101, 108, 102, 15, 0, 17, 4, 0, 29, 91, 109, 101, 116, 104, 111, 100, 93, 101, 99, 115, 45, 100, 121, 110, 97, 109, 105, 99, 45, 118, 105, 101, 119, 46, 110, 101, 120, 116, 1, 18, 1, 105, 10, 1, 64, 0, 0, 19, 4, 0, 22, 91, 99, 111, 110, 115, 116, 114, 117, 99, 116, 111, 114, 93, 101, 99, 115, 45, 119, 111, 114, 108, 100, 1, 20, 1, 64, 3, 4, 115, 101, 108, 102, 11, 10, 99, 111, 109, 112, 111, 110, 101, 110, 116, 115, 16, 15, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 105, 110, 102, 111, 115, 12, 0, 3, 4, 0, 23, 91, 109, 101, 116, 104, 111, 100, 93, 101, 99, 115, 45, 119, 111, 114, 108, 100, 46, 115, 112, 97, 119, 110, 1, 21, 1, 64, 2, 4, 115, 101, 108, 102, 11, 15, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 105, 110, 102, 111, 115, 12, 0, 13, 4, 0, 22, 91, 109, 101, 116, 104, 111, 100, 93, 101, 99, 115, 45, 119, 111, 114, 108, 100, 46, 118, 105, 101, 119, 1, 22, 3, 1, 22, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 101, 99, 115, 5, 1, 1, 64, 1, 3, 109, 115, 103, 115, 1, 0, 3, 0, 10, 104, 111, 115, 116, 45, 112, 114, 105, 110, 116, 1, 2, 1, 106, 1, 125, 0, 1, 64, 0, 0, 3, 4, 0, 7, 111, 110, 45, 105, 110, 105, 116, 1, 4, 4, 1, 26, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 101, 120, 97, 109, 112, 108, 101, 4, 0, 11, 13, 1, 0, 7, 101, 120, 97, 109, 112, 108, 101, 3, 0, 0, 0, 70, 9, 112, 114, 111, 100, 117, 99, 101, 114, 115, 1, 12, 112, 114, 111, 99, 101, 115, 115, 101, 100, 45, 98, 121, 2, 13, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 6, 48, 46, 50, 49, 46, 48, 16, 119, 105, 116, 45, 98, 105, 110, 100, 103, 101, 110, 45, 114, 117, 115, 116, 6, 48, 46, 49, 56, 46, 48]; pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 261] = [0, 97, 115, 109, 13, 0, 1, 0, 0, 25, 22, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 45, 101, 110, 99, 111, 100, 105, 110, 103, 4, 0, 7, 136, 1, 1, 65, 2, 1, 65, 7, 1, 66, 2, 1, 114, 3, 1, 120, 118, 1, 121, 118, 1, 122, 118, 4, 0, 4, 118, 101, 99, 51, 3, 0, 0, 3, 1, 23, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 109, 97, 116, 104, 5, 0, 1, 64, 1, 3, 109, 115, 103, 115, 1, 0, 3, 0, 10, 104, 111, 115, 116, 45, 112, 114, 105, 110, 116, 1, 1, 1, 106, 0, 0, 1, 64, 0, 0, 2, 4, 0, 7, 111, 110, 45, 105, 110, 105, 116, 1, 3, 4, 1, 26, 99, 111, 109, 112, 111, 110, 101, 110, 116, 58, 119, 105, 116, 103, 117, 101, 115, 116, 47, 101, 120, 97, 109, 112, 108, 101, 4, 0, 11, 13, 1, 0, 7, 101, 120, 97, 109, 112, 108, 101, 3, 0, 0, 0, 70, 9, 112, 114, 111, 100, 117, 99, 101, 114, 115, 1, 12, 112, 114, 111, 99, 101, 115, 115, 101, 100, 45, 98, 121, 2, 13, 119, 105, 116, 45, 99, 111, 109, 112, 111, 110, 101, 110, 116, 6, 48, 46, 50, 49, 46, 48, 16, 119, 105, 116, 45, 98, 105, 110, 100, 103, 101, 110, 45, 114, 117, 115, 116, 6, 48, 46, 49, 56, 46, 48];
#[inline(never)] #[inline(never)]
#[doc(hidden)] #[doc(hidden)]

View File

@ -1,20 +1,14 @@
#[allow(warnings)] #[allow(warnings)]
mod bindings; mod bindings;
use bindings::{component::witguest::ecs::{ComponentInfo, EcsWorld, Entity, WasmTypeId}, Guest}; use bindings::Guest;
//use bindings::{component::witguest::ecs::{ComponentInfo, EcsWorld, Entity, WasmTypeId}, Guest};
use common_api::{bytemuck_component_impl, Component as EcsComponent, ComponentBundle, World};
use std::{alloc::Layout, any::TypeId, mem}; use std::{alloc::Layout, any::TypeId, mem};
impl WasmTypeId {
pub fn of<T: 'static>() -> Self {
Self {
inner: unsafe { mem::transmute(TypeId::of::<T>()) }
}
}
}
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
struct Vec3 { struct Vec3 {
x: f32, x: f32,
y: f32, y: f32,
@ -27,45 +21,28 @@ impl Vec3 {
x, y, z x, y, z
} }
} }
pub fn component_info() -> ComponentInfo {
let layout = Layout::new::<Vec3>();
ComponentInfo {
size: layout.size() as u64,
alignment: layout.align() as u64,
type_id: WasmTypeId::of::<Vec3>(),
}
}
} }
bytemuck_component_impl!(Vec3);
struct Component; struct Component;
impl Guest for Component { impl Guest for Component {
fn on_init() -> Result<u8, ()> { fn on_init() -> Result<(), ()> {
let world = EcsWorld::new(); let world = World::new();
spawn_vec3(&world, Vec3::new(7.0, 30.0, 18.0));
spawn_vec3(&world, Vec3::new(90.30, 65.0, 83.0));
bindings::host_print("Spawned entities"); let mut bundle = ComponentBundle::new();
bundle.push(Vec3::new(7.0, 30.0, 18.0));
let en = world.spawn(bundle);
let view = world.view(&[Vec3::component_info()]); println!("guest spawned {:?}", en);
while let Some(mut buffer) = view.next() {
bindings::host_print(&format!("Retrieved vec3 of {} bytes long", buffer.len()));
let fetched_vec = unsafe { std::ptr::read(buffer.as_mut_ptr().cast::<Vec3>()) }; let mut view = world.view(&[Vec3::component_info()]);
bindings::host_print(&format!("Retrieved vec3: {:?}", fetched_vec)); while let Some(c) = view.next::<Vec3>() {
println!("Retrieved vec3: {:?}", c);
} }
Ok(0) Ok(())
} }
}
fn spawn_vec3(world: &EcsWorld, v: Vec3) -> Entity {
let comps = &mut [v,];
let info = &[Vec3::component_info()];
// no fucking clue if this will work, but lets try and it see
let comps = comps.as_mut_ptr().cast::<u8>();
let comps = unsafe { Vec::from_raw_parts(comps, mem::size_of::<Vec3>(), 1) };
world.spawn(&comps, info)
} }

View File

@ -8,48 +8,10 @@ interface math {
} }
} }
interface ecs {
//use math.{vec3};
record entity-id {
id: u64,
}
record entity {
id: entity-id,
generation: u64,
}
record wasm-type-id {
inner: tuple<u64, u64>,
}
record component-info {
size: u64,
alignment: u64,
type-id: wasm-type-id, // a u128
}
resource ecs-dynamic-view {
constructor(wrld: borrow<ecs-world>, component-infos: list<component-info>);
next: func() -> option<list<u8>>;
}
resource ecs-world {
constructor();
// expects components to be tightly packed in the same order of component-infos
spawn: func(components: list<u8>, component-infos: list<component-info>) -> entity;
view: func(component-infos: list<component-info>) -> ecs-dynamic-view;
}
}
/// An example world for the component to target. /// An example world for the component to target.
world example { world example {
import math; import math;
import ecs;
import host-print: func(msg: string); import host-print: func(msg: string);
export on-init: func() -> result<u8>; export on-init: func() -> result;
} }