Retrieves added + New ECS memory layout for plugins

This commit is contained in:
ccgauche 2021-02-17 00:11:05 +01:00 committed by Marcel Märtens
parent 1597fcc79e
commit 06aa7ab70c
17 changed files with 328 additions and 171 deletions

1
Cargo.lock generated
View File

@ -5699,6 +5699,7 @@ name = "veloren-plugin-api"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"serde", "serde",
"veloren-common",
] ]
[[package]] [[package]]

BIN
assets/plugins/hello.wasm Normal file

Binary file not shown.

BIN
assets/plugins/plugin1.plugin.tar (Stored with Git LFS)

Binary file not shown.

View File

@ -13,6 +13,11 @@ bin_csv = ["csv", "structopt"]
default = ["simd"] default = ["simd"]
[dependencies] [dependencies]
# Serde
serde = { version = "1.0.110", features = ["derive", "rc"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
approx = "0.4.0" approx = "0.4.0"
arraygen = "0.1.13" arraygen = "0.1.13"
crossbeam-utils = "0.8.1" crossbeam-utils = "0.8.1"
@ -22,20 +27,34 @@ lazy_static = "1.4.0"
num-derive = "0.3" num-derive = "0.3"
num-traits = "0.2" num-traits = "0.2"
ordered-float = { version = "2.0.1", default-features = false } ordered-float = { version = "2.0.1", default-features = false }
rand = "0.8"
rayon = "1.5" rayon = "1.5"
roots = "0.0.6" roots = "0.0.6"
spin_sleep = "1.0" spin_sleep = "1.0"
tracing = { version = "0.1", default-features = false } tracing = { version = "0.1", default-features = false }
vek = { version = "=0.12.0", features = ["serde"] } vek = { version = "=0.12.0", features = ["serde"] }
uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] } uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] }
rand = "0.8"
# Assets # Assets
assets_manager = {version = "0.4.2", features = ["bincode", "ron", "json", "hot-reloading"]}
directories-next = "2.0" directories-next = "2.0"
dot_vox = "4.0" dot_vox = "4.0"
image = { version = "0.23.12", default-features = false, features = ["png"] } image = { version = "0.23.12", default-features = false, features = ["png"] }
# Assets
assets_manager = {version = "0.4.2", features = ["bincode", "ron", "json", "hot-reloading"]}
# Serde
ron = { version = "0.6", default-features = false }
serde_json = "1.0.50"
serde_repr = "0.1.6"
# esv export
csv = { version = "1.1.3", optional = true }
structopt = { version = "0.3.13", optional = true }
# Tracy
tracy-client = { version = "0.10.0", optional = true }
# Data structures # Data structures
hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] } hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] }
slotmap = { version = "1.0", features = ["serde"] } slotmap = { version = "1.0", features = ["serde"] }
@ -45,18 +64,6 @@ slab = "0.4.2"
# ECS # ECS
specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control", "nightly"], rev = "d4435bdf496cf322c74886ca09dd8795984919b4" } specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control", "nightly"], rev = "d4435bdf496cf322c74886ca09dd8795984919b4" }
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "9fab7b396acd6454585486e50ae4bfe2069858a9" } specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "9fab7b396acd6454585486e50ae4bfe2069858a9" }
# Serde
ron = { version = "0.6", default-features = false }
serde = { version = "1.0.110", features = ["derive", "rc"] }
serde_json = "1.0.50"
serde_repr = "0.1.6"
#esv export
csv = { version = "1.1.3", optional = true }
structopt = { version = "0.3.13", optional = true }
# Tracy
tracy-client = { version = "0.10.0", optional = true }
[dev-dependencies] [dev-dependencies]
#bench #bench

View File

@ -18,45 +18,66 @@
trait_alias, trait_alias,
type_alias_impl_trait type_alias_impl_trait
)] )]
#[cfg(not(target_arch = "wasm32"))]
pub mod assets; pub mod assets;
pub mod astar; #[cfg(not(target_arch = "wasm32"))] pub mod astar;
#[cfg(not(target_arch = "wasm32"))]
pub mod character; pub mod character;
pub mod clock; #[cfg(not(target_arch = "wasm32"))] pub mod clock;
pub mod cmd; #[cfg(not(target_arch = "wasm32"))] pub mod cmd;
#[cfg(not(target_arch = "wasm32"))]
pub mod combat; pub mod combat;
pub mod comp; #[cfg(not(target_arch = "wasm32"))] pub mod comp;
#[cfg(not(target_arch = "wasm32"))]
pub mod consts; pub mod consts;
#[cfg(not(target_arch = "wasm32"))]
pub mod effect; pub mod effect;
pub mod event; #[cfg(not(target_arch = "wasm32"))] pub mod event;
#[cfg(not(target_arch = "wasm32"))]
pub mod explosion; pub mod explosion;
#[cfg(not(target_arch = "wasm32"))]
pub mod figure; pub mod figure;
#[cfg(not(target_arch = "wasm32"))]
pub mod generation; pub mod generation;
pub mod grid; #[cfg(not(target_arch = "wasm32"))] pub mod grid;
#[cfg(not(target_arch = "wasm32"))]
pub mod lottery; pub mod lottery;
#[cfg(not(target_arch = "wasm32"))]
pub mod metrics; pub mod metrics;
pub mod npc; #[cfg(not(target_arch = "wasm32"))] pub mod npc;
#[cfg(not(target_arch = "wasm32"))]
pub mod outcome; pub mod outcome;
pub mod path; #[cfg(not(target_arch = "wasm32"))] pub mod path;
pub mod ray; #[cfg(not(target_arch = "wasm32"))] pub mod ray;
#[cfg(not(target_arch = "wasm32"))]
pub mod recipe; pub mod recipe;
#[cfg(not(target_arch = "wasm32"))]
pub mod region; pub mod region;
pub mod resources; pub mod resources;
pub mod rtsim; #[cfg(not(target_arch = "wasm32"))] pub mod rtsim;
#[cfg(not(target_arch = "wasm32"))]
pub mod skillset_builder; pub mod skillset_builder;
#[cfg(not(target_arch = "wasm32"))]
pub mod spiral; pub mod spiral;
#[cfg(not(target_arch = "wasm32"))]
pub mod states; pub mod states;
pub mod store; #[cfg(not(target_arch = "wasm32"))] pub mod store;
#[cfg(not(target_arch = "wasm32"))]
pub mod terrain; pub mod terrain;
pub mod time; #[cfg(not(target_arch = "wasm32"))] pub mod time;
pub mod trade; #[cfg(not(target_arch = "wasm32"))] pub mod trade;
pub mod typed; #[cfg(not(target_arch = "wasm32"))] pub mod typed;
pub mod uid; pub mod uid;
pub mod util; #[cfg(not(target_arch = "wasm32"))] pub mod util;
pub mod vol; #[cfg(not(target_arch = "wasm32"))] pub mod vol;
#[cfg(not(target_arch = "wasm32"))]
pub mod volumes; pub mod volumes;
#[cfg(not(target_arch = "wasm32"))]
pub use combat::{Damage, DamageSource, GroupTarget, Knockback, KnockbackDir}; pub use combat::{Damage, DamageSource, GroupTarget, Knockback, KnockbackDir};
#[cfg(not(target_arch = "wasm32"))]
pub use comp::inventory::loadout_builder::LoadoutBuilder; pub use comp::inventory::loadout_builder::LoadoutBuilder;
#[cfg(not(target_arch = "wasm32"))]
pub use explosion::{Explosion, RadiusEffect}; pub use explosion::{Explosion, RadiusEffect};
#[cfg(not(target_arch = "wasm32"))]
pub use skillset_builder::SkillSetBuilder; pub use skillset_builder::SkillSetBuilder;

View File

@ -1,5 +1,7 @@
#[cfg(not(target_arch = "wasm32"))]
use hashbrown::HashMap; use hashbrown::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(not(target_arch = "wasm32"))]
use specs::{ use specs::{
saveload::{Marker, MarkerAllocator}, saveload::{Marker, MarkerAllocator},
world::EntitiesRes, world::EntitiesRes,
@ -22,10 +24,12 @@ impl fmt::Display for Uid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) }
} }
#[cfg(not(target_arch = "wasm32"))]
impl Component for Uid { impl Component for Uid {
type Storage = FlaggedStorage<Self, VecStorage<Self>>; type Storage = FlaggedStorage<Self, VecStorage<Self>>;
} }
#[cfg(not(target_arch = "wasm32"))]
impl Marker for Uid { impl Marker for Uid {
type Allocator = UidAllocator; type Allocator = UidAllocator;
type Identifier = u64; type Identifier = u64;
@ -37,11 +41,14 @@ impl Marker for Uid {
} }
} }
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug)]
pub struct UidAllocator { pub struct UidAllocator {
index: u64, index: u64,
mapping: HashMap<u64, Entity>, mapping: HashMap<u64, Entity>,
} }
#[cfg(not(target_arch = "wasm32"))]
impl UidAllocator { impl UidAllocator {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@ -55,10 +62,12 @@ impl UidAllocator {
pub fn remove_entity(&mut self, id: u64) -> Option<Entity> { self.mapping.remove(&id) } pub fn remove_entity(&mut self, id: u64) -> Option<Entity> { self.mapping.remove(&id) }
} }
#[cfg(not(target_arch = "wasm32"))]
impl Default for UidAllocator { impl Default for UidAllocator {
fn default() -> Self { Self::new() } fn default() -> Self { Self::new() }
} }
#[cfg(not(target_arch = "wasm32"))]
impl MarkerAllocator<Uid> for UidAllocator { impl MarkerAllocator<Uid> for UidAllocator {
fn allocate(&mut self, entity: Entity, id: Option<u64>) -> Uid { fn allocate(&mut self, entity: Entity, id: Option<u64>) -> Uid {
let id = id.unwrap_or_else(|| { let id = id.unwrap_or_else(|| {

View File

@ -1,49 +1,105 @@
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering}; use std::sync::atomic::{AtomicI32, AtomicPtr, AtomicU32, AtomicU64, Ordering};
use serde::{Serialize, de::DeserializeOwned}; use serde::{de::DeserializeOwned, Serialize};
use specs::World;
use wasmer::{Function, Memory, Value}; use wasmer::{Function, Memory, Value};
use super::errors::{MemoryAllocationError, PluginModuleError}; use super::errors::{MemoryAllocationError, PluginModuleError};
// This structure wraps the ECS pointer to ensure safety
pub struct EcsAccessManager {
ecs_pointer: AtomicPtr<World>,
}
impl Default for EcsAccessManager {
fn default() -> Self {
Self {
ecs_pointer: AtomicPtr::new(std::ptr::null_mut::<_>())
}
}
}
impl EcsAccessManager {
pub fn execute_with<T>(&self, world: &World, func: impl FnOnce() -> T) -> T {
self.ecs_pointer.store(world as *const _ as *mut _, Ordering::SeqCst);
let out = func();
self.ecs_pointer.store(std::ptr::null_mut::<_>(), Ordering::SeqCst);
out
}
pub fn get(&self) -> Option<&World> {
// ptr::as_ref will automatically check for null
unsafe {self.ecs_pointer.load(Ordering::SeqCst).as_ref()}
}
}
pub struct MemoryManager { pub struct MemoryManager {
pub pointer: AtomicI32, pub pointer: AtomicI32,
pub length: AtomicU32 pub length: AtomicU32,
} }
impl MemoryManager { impl Default for MemoryManager {
fn default() -> Self {
pub fn new() -> Self{
Self { Self {
pointer: AtomicI32::new(0), pointer: AtomicI32::new(0),
length: AtomicU32::new(0), length: AtomicU32::new(0),
} }
} }
}
// This function check if the buffer is wide enough if not it realloc the buffer calling the `wasm_prepare_buffer` function impl MemoryManager {
// Note: There is probably optimizations that can be done using less restrictive ordering
pub fn get_pointer(&self, object_length: u32, allocator: &Function) -> Result<i32,MemoryAllocationError> { // This function check if the buffer is wide enough if not it realloc the buffer
// calling the `wasm_prepare_buffer` function Note: There is probably
// optimizations that can be done using less restrictive ordering
pub fn get_pointer(
&self,
object_length: u32,
allocator: &Function,
) -> Result<i32, MemoryAllocationError> {
if self.length.load(Ordering::SeqCst) >= object_length { if self.length.load(Ordering::SeqCst) >= object_length {
return Ok(self.pointer.load(Ordering::SeqCst)); return Ok(self.pointer.load(Ordering::SeqCst));
} }
let pointer = allocator let pointer = allocator
.call(&[Value::I32(object_length as i32)]) .call(&[Value::I32(object_length as i32)])
.map_err(MemoryAllocationError::CantAllocate)?; .map_err(MemoryAllocationError::CantAllocate)?;
let pointer= pointer[0].i32().ok_or(MemoryAllocationError::InvalidReturnType)?; let pointer = pointer[0]
.i32()
.ok_or(MemoryAllocationError::InvalidReturnType)?;
self.length.store(object_length, Ordering::SeqCst); self.length.store(object_length, Ordering::SeqCst);
self.pointer.store(pointer, Ordering::SeqCst); self.pointer.store(pointer, Ordering::SeqCst);
Ok(pointer) Ok(pointer)
} }
// This function writes an object to WASM memory returning a pointer and a length. Will realloc the buffer is not wide enough // This function writes an object to WASM memory returning a pointer and a
pub fn write_data<T: Serialize>(&self, memory: &Memory, allocator: &Function ,object: &T) -> Result<(i32,u32),PluginModuleError> { // length. Will realloc the buffer is not wide enough
self.write_bytes(memory,allocator,&bincode::serialize(object).map_err(PluginModuleError::Encoding)?) pub fn write_data<T: Serialize>(
&self,
memory: &Memory,
allocator: &Function,
object: &T,
) -> Result<(i32, u32), PluginModuleError> {
self.write_bytes(
memory,
allocator,
&bincode::serialize(object).map_err(PluginModuleError::Encoding)?,
)
} }
// This function writes an raw bytes to WASM memory returning a pointer and a length. Will realloc the buffer is not wide enough // This function writes an raw bytes to WASM memory returning a pointer and a
pub fn write_bytes(&self, memory: &Memory, allocator: &Function ,array: &[u8]) -> Result<(i32,u32),PluginModuleError> { // length. Will realloc the buffer is not wide enough
pub fn write_bytes(
&self,
memory: &Memory,
allocator: &Function,
array: &[u8],
) -> Result<(i32, u32), PluginModuleError> {
let len = array.len(); let len = array.len();
let mem_position = self.get_pointer(len as u32, allocator).map_err(PluginModuleError::MemoryAllocation)? as usize; let mem_position = self
.get_pointer(len as u32, allocator)
.map_err(PluginModuleError::MemoryAllocation)? as usize;
memory.view()[mem_position..mem_position + len] memory.view()[mem_position..mem_position + len]
.iter() .iter()
.zip(array.iter()) .zip(array.iter())
@ -52,9 +108,14 @@ impl MemoryManager {
} }
} }
// This function read data from memory at a position with the array length and converts it to an object using bincode // This function read data from memory at a position with the array length and
pub fn read_data<T: DeserializeOwned>(memory: &Memory, position: i32, length: u32) -> Result<T, bincode::Error> { // converts it to an object using bincode
bincode::deserialize(&read_bytes(memory,position,length)) pub fn read_data<T: DeserializeOwned>(
memory: &Memory,
position: i32,
length: u32,
) -> Result<T, bincode::Error> {
bincode::deserialize(&read_bytes(memory, position, length))
} }
// This function read raw bytes from memory at a position with the array length // This function read raw bytes from memory at a position with the array length
@ -63,4 +124,4 @@ pub fn read_bytes(memory: &Memory, position: i32, length: u32) -> Vec<u8> {
.iter() .iter()
.map(|x| x.get()) .map(|x| x.get())
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }

View File

@ -1,7 +1,7 @@
pub mod errors; pub mod errors;
pub mod memory_manager;
pub mod module; pub mod module;
pub mod wasm_env; pub mod wasm_env;
pub mod memory_manager;
use common::assets::ASSETS_PATH; use common::assets::ASSETS_PATH;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -1,19 +1,19 @@
use std::{collections::HashSet, convert::TryInto, marker::PhantomData, sync::{Arc, Mutex, atomic::AtomicI32}};
use specs::World; use std::{collections::HashSet, convert::TryInto, marker::PhantomData, sync::{Arc, Mutex}};
use wasmer::{
imports, Cranelift, Function, Instance, Memory, Module,
Store, Value, JIT,
};
use super::{errors::{PluginError, PluginModuleError}, memory_manager::{self, MemoryManager}, wasm_env::HostFunctionEnvironement}; use common::{comp::Player, uid::UidAllocator};
use common_net::sync::WorldSyncExt;
use specs::{World, WorldExt, saveload::MarkerAllocator};
use wasmer::{imports, Cranelift, Function, Instance, Memory, Module, Store, Value, JIT};
use plugin_api::{Action, Event}; use super::{errors::{PluginError, PluginModuleError}, memory_manager::{self, EcsAccessManager, MemoryManager}, wasm_env::HostFunctionEnvironement};
use plugin_api::{Action, Event, Retreive};
#[derive(Clone)] #[derive(Clone)]
// This structure represent the WASM State of the plugin. // This structure represent the WASM State of the plugin.
pub struct PluginModule { pub struct PluginModule {
ecs: Arc<AtomicI32>, ecs: Arc<EcsAccessManager>,
wasm_state: Arc<Mutex<Instance>>, wasm_state: Arc<Mutex<Instance>>,
memory_manager: Arc<MemoryManager>, memory_manager: Arc<MemoryManager>,
events: HashSet<String>, events: HashSet<String>,
@ -43,13 +43,42 @@ impl PluginModule {
}); });
} }
let ecs = Arc::new(AtomicI32::new(i32::MAX)); fn raw_retreive_action(env: &HostFunctionEnvironement, ptr: u32, len: u32) -> i64 {
let memory_manager = Arc::new(MemoryManager::new());
println!("HOST DEBUG 1");
// TODO: Handle correctly the error
let data: Retreive = env.read_data(ptr as _, len).unwrap();
println!("HOST DEBUG 2");
let out = match data {
Retreive::GetEntityName(e) => {
println!("HOST DEBUG 3 {:?}",env.ecs.get().is_some());
let world = env.ecs.get().expect("Can't get entity name because ECS pointer isn't set");
println!("HOST DEBUG 4 {}",world.has_value::<UidAllocator>());
println!("HOST DEBUG 5 {:?}",&*world.read_resource::<UidAllocator>());
let player = world.read_resource::<UidAllocator>().retrieve_entity_internal(e.0).expect("Invalid uid");
println!("HOST DEBUG 6");
format!("{:?}",world.read_component::<Player>().get(player))
}
};
println!("{}",out);
let (ptr,len) = env.write_data(&out).unwrap();
to_i64(ptr, len as _)
}
fn dbg(a: i32) {
println!("WASM DEBUG: {}",a);
}
let ecs = Arc::new(EcsAccessManager::default());
let memory_manager = Arc::new(MemoryManager::default());
// Create an import object. // Create an import object.
let import_object = imports! { let import_object = imports! {
"env" => { "env" => {
"raw_emit_actions" => Function::new_native_with_env(&store, HostFunctionEnvironement::new(name.clone(), ecs.clone(),memory_manager.clone()), raw_emit_actions), "raw_emit_actions" => Function::new_native_with_env(&store, HostFunctionEnvironement::new(name.clone(), ecs.clone(),memory_manager.clone()), raw_emit_actions),
"raw_retreive_action" => Function::new_native_with_env(&store, HostFunctionEnvironement::new(name.clone(), ecs.clone(),memory_manager.clone()), raw_retreive_action),
"dbg" => Function::new_native(&store, dbg),
} }
}; };
@ -59,8 +88,16 @@ impl PluginModule {
Ok(Self { Ok(Self {
memory_manager, memory_manager,
ecs, ecs,
memory: instance.exports.get_memory("memory").map_err(PluginModuleError::MemoryUninit)?.clone(), memory: instance
allocator: instance.exports.get_function("wasm_prepare_buffer").map_err(PluginModuleError::MemoryUninit)?.clone(), .exports
.get_memory("memory")
.map_err(PluginModuleError::MemoryUninit)?
.clone(),
allocator: instance
.exports
.get_function("wasm_prepare_buffer")
.map_err(PluginModuleError::MemoryUninit)?
.clone(),
events: instance events: instance
.exports .exports
.iter() .iter()
@ -86,21 +123,17 @@ impl PluginModule {
return None; return None;
} }
// Store the ECS Pointer for later use in `retreives` // Store the ECS Pointer for later use in `retreives`
self.ecs.store((&ecs) as *const _ as i32, std::sync::atomic::Ordering::SeqCst); let bytes = match self.ecs.execute_with(ecs, || {
let bytes = {
let mut state = self.wasm_state.lock().unwrap(); let mut state = self.wasm_state.lock().unwrap();
match execute_raw(self,&mut state,event_name,&request.bytes) { execute_raw(self, &mut state, event_name, &request.bytes)
Ok(e) => e, }) {
Err(e) => return Some(Err(e)), Ok(e) => e,
} Err(e) => return Some(Err(e)),
}; };
// Remove the ECS Pointer to avoid UB
self.ecs.store(i32::MAX, std::sync::atomic::Ordering::SeqCst);
Some(bincode::deserialize(&bytes).map_err(PluginModuleError::Encoding)) Some(bincode::deserialize(&bytes).map_err(PluginModuleError::Encoding))
} }
} }
// This structure represent a Pre-encoded event object (Useful to avoid // This structure represent a Pre-encoded event object (Useful to avoid
// reencoding for each module in every plugin) // reencoding for each module in every plugin)
pub struct PreparedEventQuery<T> { pub struct PreparedEventQuery<T> {
@ -122,9 +155,18 @@ impl<T: Event> PreparedEventQuery<T> {
} }
} }
fn from_i64(i: i64) -> (i32,i32) { fn from_i64(i: i64) -> (i32, i32) {
let i = i.to_le_bytes(); let i = i.to_le_bytes();
(i32::from_le_bytes(i[0..4].try_into().unwrap()),i32::from_le_bytes(i[4..8].try_into().unwrap())) (
i32::from_le_bytes(i[0..4].try_into().unwrap()),
i32::from_le_bytes(i[4..8].try_into().unwrap()),
)
}
pub fn to_i64(a: i32, b: i32) -> i64 {
let a = a.to_le_bytes();
let b = b.to_le_bytes();
i64::from_le_bytes([a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3]])
} }
// This function is not public because this function should not be used without // This function is not public because this function should not be used without
@ -136,10 +178,13 @@ fn execute_raw(
event_name: &str, event_name: &str,
bytes: &[u8], bytes: &[u8],
) -> Result<Vec<u8>, PluginModuleError> { ) -> Result<Vec<u8>, PluginModuleError> {
// This write into memory `bytes` using allocation if necessary returning a
// pointer and a length
// This write into memory `bytes` using allocation if necessary returning a pointer and a length let (mem_position, len) =
module
let (mem_position,len) = module.memory_manager.write_bytes(&module.memory, &module.allocator, bytes)?; .memory_manager
.write_bytes(&module.memory, &module.allocator, bytes)?;
// This gets the event function from module exports // This gets the event function from module exports
@ -153,16 +198,23 @@ fn execute_raw(
let function_result = func let function_result = func
.call(&[Value::I32(mem_position as i32), Value::I32(len as i32)]) .call(&[Value::I32(mem_position as i32), Value::I32(len as i32)])
.map_err(PluginModuleError::RunFunction)?; .map_err(PluginModuleError::RunFunction)?;
// Waiting for `multi-value` to be added to LLVM. So we encode the two i32 as an i64
let (pointer,length) = from_i64(function_result[0] // Waiting for `multi-value` to be added to LLVM. So we encode the two i32 as an
.i64() // i64
.ok_or_else(PluginModuleError::InvalidArgumentType)?);
let (pointer, length) = from_i64(
function_result[0]
.i64()
.ok_or_else(PluginModuleError::InvalidArgumentType)?,
);
// We read the return object and deserialize it // We read the return object and deserialize it
Ok(memory_manager::read_bytes(&module.memory, pointer, length as u32)) Ok(memory_manager::read_bytes(
&module.memory,
pointer,
length as u32,
))
} }
fn handle_actions(actions: Vec<Action>) { fn handle_actions(actions: Vec<Action>) {

View File

@ -1,22 +1,23 @@
use std::sync::Arc; use std::sync::{atomic::AtomicI32, Arc};
use std::sync::atomic::AtomicI32;
use serde::{Serialize, de::DeserializeOwned}; use serde::{de::DeserializeOwned, Serialize};
use wasmer::{Function, HostEnvInitError, Instance, LazyInit, Memory, WasmerEnv}; use wasmer::{Function, HostEnvInitError, Instance, LazyInit, Memory, WasmerEnv};
use super::{errors::PluginModuleError, memory_manager::{self, MemoryManager}}; use super::{errors::PluginModuleError, memory_manager::{self, EcsAccessManager, MemoryManager}};
#[derive(Clone)] #[derive(Clone)]
pub struct HostFunctionEnvironement { pub struct HostFunctionEnvironement {
pub ecs: Arc<AtomicI32>, // This represent the pointer to the ECS object (set to i32::MAX if to ECS is availible) pub ecs: Arc<EcsAccessManager>, /* This represent the pointer to the ECS object (set to i32::MAX if
* to ECS is availible) */
pub memory: LazyInit<Memory>, // This object represent the WASM Memory pub memory: LazyInit<Memory>, // This object represent the WASM Memory
pub allocator: LazyInit<Function>, // Linked to: wasm_prepare_buffer pub allocator: LazyInit<Function>, // Linked to: wasm_prepare_buffer
pub memory_manager: Arc<MemoryManager>, // This object represent the current buffer size and pointer pub memory_manager: Arc<MemoryManager>, /* This object represent the current buffer size and
* pointer */
pub name: String, // This represent the plugin name pub name: String, // This represent the plugin name
} }
impl HostFunctionEnvironement { impl HostFunctionEnvironement {
pub fn new(name: String,ecs: Arc<AtomicI32>,memory_manager: Arc<MemoryManager>) -> Self { pub fn new(name: String, ecs: Arc<EcsAccessManager>, memory_manager: Arc<MemoryManager>) -> Self {
Self { Self {
memory_manager, memory_manager,
ecs, ecs,
@ -26,13 +27,23 @@ impl HostFunctionEnvironement {
} }
} }
// This function is a safe interface to WASM memory that writes data to the memory returning a pointer and length // This function is a safe interface to WASM memory that writes data to the
pub fn write_data<T: Serialize>(&self, object: &T) -> Result<(i32,u32),PluginModuleError> { // memory returning a pointer and length
self.memory_manager.write_data(self.memory.get_ref().unwrap(), self.allocator.get_ref().unwrap(), object) pub fn write_data<T: Serialize>(&self, object: &T) -> Result<(i32, u32), PluginModuleError> {
self.memory_manager.write_data(
self.memory.get_ref().unwrap(),
self.allocator.get_ref().unwrap(),
object,
)
} }
// This function is a safe interface to WASM memory that reads memory from pointer and length returning an object // This function is a safe interface to WASM memory that reads memory from
pub fn read_data<T: DeserializeOwned>(&self, position: i32, length: u32) -> Result<T, bincode::Error> { // pointer and length returning an object
pub fn read_data<T: DeserializeOwned>(
&self,
position: i32,
length: u32,
) -> Result<T, bincode::Error> {
memory_manager::read_data(self.memory.get_ref().unwrap(), position, length) memory_manager::read_data(self.memory.get_ref().unwrap(), position, length)
} }
} }
@ -41,8 +52,11 @@ impl WasmerEnv for HostFunctionEnvironement {
fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> { fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> {
let memory = instance.exports.get_memory("memory").unwrap(); let memory = instance.exports.get_memory("memory").unwrap();
self.memory.initialize(memory.clone()); self.memory.initialize(memory.clone());
let allocator = instance.exports.get_function("wasm_prepare_buffer").expect("Can't get allocator"); let allocator = instance
.exports
.get_function("wasm_prepare_buffer")
.expect("Can't get allocator");
self.allocator.initialize(allocator.clone()); self.allocator.initialize(allocator.clone());
Ok(()) Ok(())
} }
} }

View File

@ -208,12 +208,14 @@ impl State {
#[cfg(feature = "plugins")] #[cfg(feature = "plugins")]
ecs.insert(match PluginMgr::from_assets() { ecs.insert(match PluginMgr::from_assets() {
Ok(plugin_mgr) => { Ok(plugin_mgr) => {
if let Err(e) = plugin_mgr if let Err(e) =
.execute_event(&ecs,"on_load", &plugin_api::event::PluginLoadEvent { game_mode: match game_mode { plugin_mgr.execute_event(&ecs, "on_load", &plugin_api::event::PluginLoadEvent {
resources::GameMode::Server => plugin_api::GameMode::Server, game_mode: match game_mode {
resources::GameMode::Client => plugin_api::GameMode::Client, resources::GameMode::Server => plugin_api::GameMode::Server,
resources::GameMode::Singleplayer => plugin_api::GameMode::Singleplayer, resources::GameMode::Client => plugin_api::GameMode::Client,
} }) resources::GameMode::Singleplayer => plugin_api::GameMode::Singleplayer,
},
})
{ {
tracing::error!(?e, "Failed to run plugin init"); tracing::error!(?e, "Failed to run plugin init");
tracing::info!( tracing::info!(

View File

@ -6,3 +6,4 @@ edition = "2018"
[dependencies] [dependencies]
serde = { version = "1.0.118", features = ["derive"] } serde = { version = "1.0.118", features = ["derive"] }
common = { package = "veloren-common", path = "../../common", features = ["no-assets"] }

View File

@ -1,8 +1,7 @@
use std::fmt;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub use common::{resources::GameMode, uid::Uid};
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub enum Action { pub enum Action {
ServerClose, ServerClose,
@ -20,35 +19,6 @@ pub trait Event: Serialize + DeserializeOwned + Send + Sync {
type Response: Serialize + DeserializeOwned + Send + Sync; type Response: Serialize + DeserializeOwned + Send + Sync;
} }
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Uid(pub u64);
impl Into<u64> for Uid {
fn into(self) -> u64 { self.0 }
}
impl From<u64> for Uid {
fn from(uid: u64) -> Self { Self(uid) }
}
impl fmt::Display for Uid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) }
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum GameMode {
/// The game is being played in server mode (i.e: the code is running
/// server-side)
Server,
/// The game is being played in client mode (i.e: the code is running
/// client-side)
Client,
/// The game is being played in singleplayer mode (i.e: both client and
/// server at once)
// To be used later when we no longer start up an entirely new server for singleplayer
Singleplayer,
}
pub mod event { pub mod event {
use super::*; use super::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -3,7 +3,7 @@ use veloren_plugin_rt::{
*, *,
}; };
#[veloren_plugin_rt::event_handler] #[event_handler]
pub fn on_load(load: PluginLoadEvent) { pub fn on_load(load: PluginLoadEvent) {
match load.game_mode { match load.game_mode {
GameMode::Server => emit_action(Action::Print("Hello, server!".to_owned())), GameMode::Server => emit_action(Action::Print("Hello, server!".to_owned())),
@ -15,13 +15,11 @@ pub fn on_load(load: PluginLoadEvent) {
#[event_handler] #[event_handler]
pub fn on_command_testplugin(command: ChatCommandEvent) -> Result<Vec<String>, String> { pub fn on_command_testplugin(command: ChatCommandEvent) -> Result<Vec<String>, String> {
Ok(vec![format!( Ok(vec![format!(
"Player of id {:?} sended command with args {:?}", "Player of id {:?} named {} sended command with args {:?}",
command.player, command.command_args command.player, command.player.get_entity_name(), command.command_args
)]) )])
} }
#[event_handler] #[event_handler]
pub fn on_player_join(input: PlayerJoinEvent) -> PlayerJoinResult { pub fn on_player_join(input: PlayerJoinEvent) -> PlayerJoinResult {
emit_action(Action::PlayerSendMessage( emit_action(Action::PlayerSendMessage(

View File

@ -4,6 +4,8 @@ pub extern crate plugin_derive;
pub mod retreive; pub mod retreive;
pub use retreive::*;
use std::convert::TryInto; use std::convert::TryInto;
pub use retreive::*; pub use retreive::*;
@ -16,22 +18,28 @@ use serde::{de::DeserializeOwned, Serialize};
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
extern "C" { extern "C" {
fn raw_emit_actions(ptr: *const u8, len: usize); fn raw_emit_actions(ptr: *const u8, len: usize);
//fn raw_retreive_action(ptr: *const u8, len: usize) -> (i32, u32); fn raw_retreive_action(ptr: *const u8, len: usize) -> i64;
pub fn dbg(i: i32);
} }
// pub fn retreive_action<T: DeserializeOwned>(_actions: &api::Retreive) -> Result<T,bincode::Error> { pub fn retreive_action<T: DeserializeOwned>(_actions: &api::Retreive) -> Result<T,bincode::Error> {
// #[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
// { {
// let ret = bincode::serialize(&_actions).expect("Can't serialize action in emit"); unsafe{dbg(0);}
// unsafe { let ret = bincode::serialize(&_actions).expect("Can't serialize action in emit");
// let (ptr,len) = raw_retreive_action(ret.as_ptr(), ret.len()); unsafe{dbg(1);}
// let a = ::std::slice::from_raw_parts(ptr as _, len as _); unsafe {
// bincode::deserialize(&a) dbg(2);
// } let (ptr,len) = from_i64(raw_retreive_action(ret.as_ptr(), ret.len()));
// } dbg(3);
// #[cfg(not(target_arch = "wasm32"))] let a = ::std::slice::from_raw_parts(ptr as _, len as _);
// unreachable!() dbg(4);
// } bincode::deserialize(&a)
}
}
#[cfg(not(target_arch = "wasm32"))]
unreachable!()
}
pub fn emit_action(action: api::Action) { emit_actions(vec![action]) } pub fn emit_action(action: api::Action) { emit_actions(vec![action]) }
@ -53,10 +61,18 @@ where
bincode::deserialize(slice).map_err(|_| "Failed to deserialize function input") bincode::deserialize(slice).map_err(|_| "Failed to deserialize function input")
} }
pub fn from_i64(i: i64) -> (i32, i32) {
let i = i.to_le_bytes();
(
i32::from_le_bytes(i[0..4].try_into().unwrap()),
i32::from_le_bytes(i[4..8].try_into().unwrap()),
)
}
pub fn to_i64(a: i32, b: i32) -> i64 { pub fn to_i64(a: i32, b: i32) -> i64 {
let a = a.to_le_bytes(); let a = a.to_le_bytes();
let b = b.to_le_bytes(); let b = b.to_le_bytes();
i64::from_le_bytes([a[0],a[1],a[2],a[3],b[0],b[1],b[2],b[3]]) i64::from_le_bytes([a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3]])
} }
pub fn write_output(value: impl Serialize) -> i64 { pub fn write_output(value: impl Serialize) -> i64 {

View File

@ -1,13 +1,15 @@
use crate::api::Retreive; use crate::api::Retreive;
trait GetEntityName { pub trait GetEntityName {
fn get_entity_name(&self) -> String; fn get_entity_name(&self) -> String;
} }
impl GetEntityName for crate::api::event::Player { impl GetEntityName for crate::api::event::Player {
fn get_entity_name(&self) -> String { fn get_entity_name(&self) -> String {
// crate::retreive_action(&Retreive::GetEntityName(self.id)).expect("Can't get entity name") #[cfg(target_arch = "wasm32")]
String::new() unsafe {
crate::dbg(-1);
}
crate::retreive_action(&Retreive::GetEntityName(self.id)).expect("Can't get entity name")
} }
} }

View File

@ -1037,12 +1037,15 @@ impl Server {
command: kwd.clone(), command: kwd.clone(),
command_args: args.split(' ').map(|x| x.to_owned()).collect(), command_args: args.split(' ').map(|x| x.to_owned()).collect(),
player: plugin_api::event::Player { player: plugin_api::event::Player {
id: plugin_api::Uid((self id: plugin_api::Uid(
.state (self
.ecs() .state
.read_storage::<Uid>() .ecs()
.get(entity) .read_storage::<Uid>()
.expect("Can't get player UUID [This should never appen]")).0), .get(entity)
.expect("Can't get player UUID [This should never appen]"))
.0,
),
}, },
}, },
); );