Implement ECS resources
This commit is contained in:
parent
dcb48e9acf
commit
1d97046195
File diff suppressed because it is too large
Load Diff
|
@ -13,6 +13,8 @@ tokio = { version = "1.41.1", features = ["full"] }
|
|||
wasmtime = "26.0.1"
|
||||
wasmtime-wasi = "26.0.1"
|
||||
lyra-ecs = { path = "./lyra-engine/crates/lyra-ecs" }
|
||||
lyra-reflect = { path = "./lyra-engine/crates/lyra-reflect" }
|
||||
lyra-engine = { path = "./lyra-engine" }
|
||||
slab = "0.4.9"
|
||||
thiserror = "2.0.0"
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ pub use component::*;
|
|||
mod bundle;
|
||||
pub use bundle::*;
|
||||
|
||||
pub mod resource;
|
||||
pub use resource::EcsResource;
|
||||
|
||||
pub mod math;
|
||||
|
||||
pub use api_derive as macros;
|
||||
|
@ -29,6 +32,12 @@ impl WasmTypeId {
|
|||
inner: unsafe { mem::transmute(TypeId::of::<T>()) },
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn from_raw(id: u128) -> Self {
|
||||
Self {
|
||||
inner: unsafe { mem::transmute(id) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum OwnedBorrow<'a, T> {
|
||||
|
@ -121,6 +130,11 @@ impl<'a> World<'a> {
|
|||
self.inner.view_one(en, &infos)
|
||||
.map(|bytes| B::from_bytes(bytes))
|
||||
}
|
||||
|
||||
pub fn get_resource<T: EcsResource>(&self) -> Option<T> {
|
||||
let res = self.inner.get_resource(T::TYPE_ID);
|
||||
T::from_wasm_result(&res)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct View<B: Bundle> {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use crate::lyra::api::ecs::{WasmTypeId, WorldResourceResult};
|
||||
use super::EcsResource;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DeltaTime(f32);
|
||||
|
||||
impl Deref for DeltaTime {
|
||||
type Target = f32;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl EcsResource for DeltaTime {
|
||||
const TYPE_ID: WasmTypeId = WasmTypeId::from_raw(83716348954);
|
||||
|
||||
fn from_wasm_result(result: &WorldResourceResult) -> Option<Self> {
|
||||
match result {
|
||||
WorldResourceResult::None => None,
|
||||
WorldResourceResult::WasmResourceRep(_) => None,
|
||||
WorldResourceResult::Bytes(vec) => Some(Self(*bytemuck::from_bytes::<f32>(&vec))),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
use crate::lyra::api::ecs::{WasmTypeId, WorldResourceResult};
|
||||
|
||||
mod dt;
|
||||
pub use dt::*;
|
||||
|
||||
pub trait EcsResource: Sized {
|
||||
const TYPE_ID: WasmTypeId;
|
||||
|
||||
fn from_wasm_result(result: &WorldResourceResult) -> Option<Self>;
|
||||
}
|
||||
|
|
@ -37,6 +37,12 @@ interface ecs {
|
|||
next: func() -> option<tuple<entity, list<u8>>>;
|
||||
}
|
||||
|
||||
variant world-resource-result {
|
||||
none,
|
||||
wasm-resource-rep(u32),
|
||||
bytes(list<u8>),
|
||||
}
|
||||
|
||||
resource ecs-world {
|
||||
constructor();
|
||||
|
||||
|
@ -76,6 +82,8 @@ interface ecs {
|
|||
/// Returns: A row of components serialized as bytes. The buffer is tighly packed.
|
||||
view-one: func(en: entity, component-infos: list<component-info>) -> option<list<u8>>;
|
||||
|
||||
get-resource: func(type-id: wasm-type-id) -> world-resource-result;
|
||||
|
||||
//with_system: func(stage: string, component-infos: list<component-info>, system: func(components: list<u8>));
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use common_api::{math::{Vec3, Vec4}, World};
|
||||
use common_api::{math::{Vec3, Vec4}, resource::DeltaTime, World};
|
||||
|
||||
wit_bindgen::generate!({
|
||||
world: "example",
|
||||
|
@ -22,6 +22,9 @@ impl Guest for Component {
|
|||
.unwrap().unwrap();
|
||||
println!("Found entity at {pos3:?}");
|
||||
|
||||
let dt = world.get_resource::<DeltaTime>().unwrap();
|
||||
println!("dt is {}", *dt);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,12 @@ interface ecs {
|
|||
next: func() -> option<tuple<entity, list<u8>>>;
|
||||
}
|
||||
|
||||
variant world-resource-result {
|
||||
none,
|
||||
wasm-resource-rep(u32),
|
||||
bytes(list<u8>),
|
||||
}
|
||||
|
||||
resource ecs-world {
|
||||
constructor();
|
||||
|
||||
|
@ -76,6 +82,8 @@ interface ecs {
|
|||
/// Returns: A row of components serialized as bytes. The buffer is tighly packed.
|
||||
view-one: func(en: entity, component-infos: list<component-info>) -> option<list<u8>>;
|
||||
|
||||
get-resource: func(type-id: wasm-type-id) -> world-resource-result;
|
||||
|
||||
//with_system: func(stage: string, component-infos: list<component-info>, system: func(components: list<u8>));
|
||||
}
|
||||
}
|
101
src/main.rs
101
src/main.rs
|
@ -1,5 +1,7 @@
|
|||
use std::alloc::Layout;
|
||||
use std::any::TypeId;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
|
@ -11,12 +13,17 @@ use lyra_ecs::{query::dynamic::DynamicViewOne, DynTypeId, World};
|
|||
|
||||
use lyra_ecs as ecs;
|
||||
|
||||
use ::lyra_engine::DeltaTime;
|
||||
use lyra_reflect::{FromType, ReflectedResource, RegisteredType, TypeRegistry};
|
||||
use slab::Slab;
|
||||
use thiserror::Error;
|
||||
use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiView};
|
||||
|
||||
use wasmtime::component::Resource as WasmResource;
|
||||
|
||||
mod proxy;
|
||||
pub use proxy::*;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) mod lyra_engine {
|
||||
pub use lyra_ecs as ecs;
|
||||
|
@ -241,6 +248,27 @@ impl wasm_ecs::HostEcsWorld for Imports {
|
|||
}
|
||||
}
|
||||
|
||||
async fn get_resource(&mut self, this: wasmtime::component::Resource<wasm_ecs::EcsWorld>, type_id: wasm_ecs::WasmTypeId) -> wasm_ecs::WorldResourceResult {
|
||||
let world_entry = self
|
||||
.world_slab
|
||||
.try_remove(this.rep() as _)
|
||||
.ok_or(WasmError::InvalidResourceHandle("EcsWorld")).unwrap();
|
||||
let world = &world_entry.world;
|
||||
|
||||
let native_type = world.get_resource::<GuestTypeLookup>().unwrap();
|
||||
let native_tid = native_type.lookup_type_id(type_id)
|
||||
.expect("failed to find native type id of wasm type");
|
||||
|
||||
let reg = world.get_resource::<TypeRegistry>().unwrap();
|
||||
let data = reg.get_type(native_tid).unwrap();
|
||||
let proxy = data.get_data::<WasmProxied>().unwrap();
|
||||
|
||||
let refl = data.get_data::<ReflectedResource>().unwrap();
|
||||
let refl = refl.reflect(world).unwrap();
|
||||
let res = proxy.marshal_to_bytes(refl.deref());
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
async fn drop(
|
||||
&mut self,
|
||||
|
@ -363,7 +391,10 @@ impl wasm_ecs::HostEcsDynamicView for Imports {
|
|||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct GuestTypeLookup(HashMap<u128, lyra_ecs::ComponentInfo>);
|
||||
struct GuestTypeLookup {
|
||||
infos: HashMap<u128, lyra_ecs::ComponentInfo>,
|
||||
type_ids: HashMap<u128, TypeId>,
|
||||
}
|
||||
|
||||
impl GuestTypeLookup {
|
||||
/// Try to find the native type's component info.
|
||||
|
@ -372,7 +403,7 @@ impl GuestTypeLookup {
|
|||
/// will be returned. If info is found, the size and alignment will be verified to match using
|
||||
/// asserts and the info of the native type will be returned.
|
||||
fn lookup_info(&self, info: lyra_ecs::ComponentInfo) -> lyra_ecs::ComponentInfo {
|
||||
match self.0.get(&info.type_id().as_unknown().unwrap()) {
|
||||
match self.infos.get(&info.type_id().as_unknown().unwrap()) {
|
||||
Some(native) => {
|
||||
let native_align = native.layout().align() as usize;
|
||||
let native_size = native.layout().size() as usize;
|
||||
|
@ -386,6 +417,12 @@ impl GuestTypeLookup {
|
|||
None => info
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_type_id(&self, wasm_id: wasm_ecs::WasmTypeId) -> Option<TypeId> {
|
||||
// SAFETY: a (u64, u64) is the same as a u128
|
||||
let id: u128 = unsafe { std::mem::transmute(wasm_id) };
|
||||
self.type_ids.get(&id).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -425,17 +462,30 @@ async fn main() -> wasmtime::Result<()> {
|
|||
);
|
||||
|
||||
// Load the component from disk
|
||||
//let bytes = std::fs::read("target/wasm32-wasip1/debug/witguest-component.wasm")?;
|
||||
let bytes = std::fs::read("guests/csharp/dotnet-guest-test/bin/Debug/net9.0/wasi-wasm/native/dotnet-guest-test.wasm")?;
|
||||
let bytes = std::fs::read("target/wasm32-wasip1/debug/witguest-component.wasm")?;
|
||||
//let bytes = std::fs::read("guests/csharp/dotnet-guest-test/bin/Debug/net9.0/wasi-wasm/native/dotnet-guest-test.wasm")?;
|
||||
let component = wasmtime::component::Component::new(&engine, bytes)?;
|
||||
|
||||
let (script_en, (world_res_a, world_res_b)) = {
|
||||
let mut world = World::new();
|
||||
|
||||
world.add_resource(DeltaTime::from(100.00));
|
||||
|
||||
let mut lookup = GuestTypeLookup::default();
|
||||
lookup.0.insert(4124409524, lyra_ecs::ComponentInfo::new::<Vec3>());
|
||||
lookup.infos.insert(4124409524, lyra_ecs::ComponentInfo::new::<Vec3>());
|
||||
lookup.type_ids.insert(83716348954, TypeId::of::<::lyra_engine::DeltaTime>());
|
||||
world.add_resource(lookup);
|
||||
|
||||
{
|
||||
let mut reg = world.get_resource_or_default::<TypeRegistry>();
|
||||
|
||||
let mut dt_type = RegisteredType::new();
|
||||
dt_type.add_data(<ReflectedResource as FromType::<DeltaTime>>::from_type());
|
||||
dt_type.add_data(<WasmProxied as FromType::<DeltaTime>>::from_type());
|
||||
|
||||
reg.add_registered_type(TypeId::of::<DeltaTime>(), dt_type);
|
||||
}
|
||||
|
||||
let script_en = world.spawn(());
|
||||
|
||||
let data = store.data_mut();
|
||||
|
@ -458,31 +508,32 @@ async fn main() -> wasmtime::Result<()> {
|
|||
println!("RUST: Guest is done");
|
||||
|
||||
let rep = world_res_b.rep();
|
||||
let w = store.data().world_slab.get(rep as _).unwrap();
|
||||
let w = &w.world;
|
||||
println!("RUST: Got {} archetypes", w.archetype_count());
|
||||
for a in w.archetypes.values() {
|
||||
println!("RUST: Archetype {}", a.id().0);
|
||||
for col in &a.columns {
|
||||
println!("RUST: Column type id: {:?}", col.info.type_id());
|
||||
if let Some(w) = store.data().world_slab.get(rep as _) {
|
||||
let w = &w.world;
|
||||
println!("RUST: Got {} archetypes", w.archetype_count());
|
||||
for a in w.archetypes.values() {
|
||||
println!("RUST: Archetype {}", a.id().0);
|
||||
for col in &a.columns {
|
||||
println!("RUST: Column type id: {:?}", col.info.type_id());
|
||||
|
||||
if col.info.type_id().is_id(DynTypeId::Unknown(4124409524)) {
|
||||
println!("RUST: Found C# Vec3");
|
||||
let pos = unsafe { col.get::<Vec3>(0) };
|
||||
println!("RUST: Entity 0 pos: {:?}", *pos);
|
||||
if col.info.type_id().is_id(DynTypeId::Unknown(4124409524)) {
|
||||
println!("RUST: Found C# Vec3");
|
||||
let pos = unsafe { col.get::<Vec3>(0) };
|
||||
println!("RUST: Entity 0 pos: {:?}", *pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut dv = w.dynamic_view();
|
||||
dv.push(QueryDynamicType::from_info(lyra_ecs::ComponentInfo::new::<Vec3>()));
|
||||
let iter = dv.into_iter();
|
||||
let mut dv = w.dynamic_view();
|
||||
dv.push(QueryDynamicType::from_info(lyra_ecs::ComponentInfo::new::<Vec3>()));
|
||||
let iter = dv.into_iter();
|
||||
|
||||
for (_, comps) in iter {
|
||||
let first = comps.first().unwrap();
|
||||
unsafe {
|
||||
let v: Vec3 = std::ptr::read(first.ptr.as_ptr() as _);
|
||||
println!("RUST: Found native Vec3! {:?}", v);
|
||||
for (_, comps) in iter {
|
||||
let first = comps.first().unwrap();
|
||||
unsafe {
|
||||
let v: Vec3 = std::ptr::read(first.ptr.as_ptr() as _);
|
||||
println!("RUST: Found native Vec3! {:?}", v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
use crate::lyra::api::ecs as wasm_ecs;
|
||||
use common_api::bytemuck;
|
||||
use lyra_engine::DeltaTime;
|
||||
use lyra_reflect::{FromType, Reflect};
|
||||
|
||||
pub trait WasmProxy {
|
||||
fn marshal_to_bytes(&self) -> wasm_ecs::WorldResourceResult;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WasmProxied {
|
||||
fn_marshal: for<'a> fn (&'a dyn Reflect) -> wasm_ecs::WorldResourceResult,
|
||||
}
|
||||
|
||||
impl WasmProxied {
|
||||
pub fn marshal_to_bytes(&self, reflected: &dyn Reflect) -> wasm_ecs::WorldResourceResult {
|
||||
(self.fn_marshal)(reflected)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromType<T> for WasmProxied
|
||||
where
|
||||
T: WasmProxy + 'static
|
||||
{
|
||||
fn from_type() -> Self {
|
||||
WasmProxied {
|
||||
fn_marshal: |reflect| {
|
||||
let this: &T = reflect.as_any().downcast_ref().unwrap();
|
||||
this.marshal_to_bytes()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmProxy for DeltaTime {
|
||||
fn marshal_to_bytes(&self) -> wasm_ecs::WorldResourceResult {
|
||||
wasm_ecs::WorldResourceResult::Bytes(bytemuck::bytes_of(&**self).to_vec())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue