diff --git a/Cargo.lock b/Cargo.lock index d394200d4c..d8426940ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5699,7 +5699,6 @@ name = "veloren-plugin-api" version = "0.1.0" dependencies = [ "serde", - "veloren-common", ] [[package]] diff --git a/assets/plugins/plugin1.plugin.tar b/assets/plugins/plugin1.plugin.tar new file mode 100644 index 0000000000..cec6c45c96 Binary files /dev/null and b/assets/plugins/plugin1.plugin.tar differ diff --git a/common/sys/src/plugin/module.rs b/common/sys/src/plugin/module.rs index ae5fb2483a..fe314936cc 100644 --- a/common/sys/src/plugin/module.rs +++ b/common/sys/src/plugin/module.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, marker::PhantomData, sync::{Arc, Mutex, atomic::AtomicI32}}; +use std::{collections::HashSet, convert::TryInto, marker::PhantomData, sync::{Arc, Mutex, atomic::AtomicI32}}; use specs::World; use wasmer::{ @@ -85,6 +85,7 @@ impl PluginModule { if !self.events.contains(event_name) { return None; } + // Store the ECS Pointer for later use in `retreives` self.ecs.store((&ecs) as *const _ as i32, std::sync::atomic::Ordering::SeqCst); let bytes = { let mut state = self.wasm_state.lock().unwrap(); @@ -93,6 +94,7 @@ impl PluginModule { 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)) } @@ -120,6 +122,11 @@ impl PreparedEventQuery { } } +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())) +} + // This function is not public because this function should not be used without // an interface to limit unsafe behaviours #[allow(clippy::needless_range_loop)] @@ -129,25 +136,33 @@ fn execute_raw( event_name: &str, bytes: &[u8], ) -> Result, PluginModuleError> { + + // This write into memory `bytes` using allocation if necessary returning a pointer and a length + let (mem_position,len) = module.memory_manager.write_bytes(&module.memory, &module.allocator, bytes)?; + // This gets the event function from module exports + let func = instance .exports .get_function(event_name) .map_err(PluginModuleError::MemoryUninit)?; + // We call the function with the pointer and the length + let function_result = func .call(&[Value::I32(mem_position as i32), Value::I32(len as i32)]) .map_err(PluginModuleError::RunFunction)?; - - let pointer = function_result[0] - .i32() - .ok_or_else(PluginModuleError::InvalidArgumentType)?; - let length = function_result[1] - .i32() - .ok_or_else(PluginModuleError::InvalidArgumentType)? as u32; + + // Waiting for `multi-value` to be added to LLVM. So we encode the two i32 as an i64 - Ok(memory_manager::read_bytes(&module.memory, pointer, length)) + let (pointer,length) = from_i64(function_result[0] + .i64() + .ok_or_else(PluginModuleError::InvalidArgumentType)?); + + // We read the return object and deserialize it + + Ok(memory_manager::read_bytes(&module.memory, pointer, length as u32)) } fn handle_actions(actions: Vec) { diff --git a/common/sys/src/state.rs b/common/sys/src/state.rs index 22bded5950..a58cdfd26f 100644 --- a/common/sys/src/state.rs +++ b/common/sys/src/state.rs @@ -209,7 +209,11 @@ impl State { ecs.insert(match PluginMgr::from_assets() { Ok(plugin_mgr) => { if let Err(e) = plugin_mgr - .execute_event(&ecs,"on_load", &plugin_api::event::PluginLoadEvent { game_mode }) + .execute_event(&ecs,"on_load", &plugin_api::event::PluginLoadEvent { game_mode: match game_mode { + resources::GameMode::Server => plugin_api::GameMode::Server, + resources::GameMode::Client => plugin_api::GameMode::Client, + resources::GameMode::Singleplayer => plugin_api::GameMode::Singleplayer, + } }) { tracing::error!(?e, "Failed to run plugin init"); tracing::info!( diff --git a/plugin/api/Cargo.toml b/plugin/api/Cargo.toml index d35c2a3a30..db865e87e5 100644 --- a/plugin/api/Cargo.toml +++ b/plugin/api/Cargo.toml @@ -5,5 +5,4 @@ authors = ["ccgauche "] edition = "2018" [dependencies] -common = { package = "veloren-common", path = "../../common", features = ["no-assets"] } serde = { version = "1.0.118", features = ["derive"] } diff --git a/plugin/api/src/lib.rs b/plugin/api/src/lib.rs index bb094c73e9..510169de17 100644 --- a/plugin/api/src/lib.rs +++ b/plugin/api/src/lib.rs @@ -1,4 +1,6 @@ -use common::uid::Uid; + +use std::fmt; + use serde::{de::DeserializeOwned, Deserialize, Serialize}; #[derive(Deserialize, Serialize, Debug)] @@ -18,7 +20,34 @@ pub trait Event: Serialize + DeserializeOwned + Send + Sync { type Response: Serialize + DeserializeOwned + Send + Sync; } -pub use common::resources::GameMode; +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub struct Uid(pub u64); + +impl Into for Uid { + fn into(self) -> u64 { self.0 } +} + +impl From 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 { use super::*; diff --git a/plugin/derive/src/lib.rs b/plugin/derive/src/lib.rs index de4e96bd21..69fd927fa5 100644 --- a/plugin/derive/src/lib.rs +++ b/plugin/derive/src/lib.rs @@ -16,8 +16,8 @@ pub fn event_handler(_args: TokenStream, item: TokenStream) -> TokenStream { let out: proc_macro2::TokenStream = quote! { #[allow(clippy::unnecessary_wraps)] #[no_mangle] - pub fn #fn_name(intern__ptr: i32, intern__len: u32) -> (i32,i32) { - let input = ::veloren_plugin_rt::read_input(intern__ptr,intern__len).unwrap(); + pub fn #fn_name(intern__ptr: i32, intern__len: i32) -> i64 { + let input = ::veloren_plugin_rt::read_input(intern__ptr,intern__len as u32).unwrap(); #[inline] fn inner(#fn_args) #fn_return { #fn_body diff --git a/plugin/rt/src/lib.rs b/plugin/rt/src/lib.rs index 3a85a9275f..d8aa0d4006 100644 --- a/plugin/rt/src/lib.rs +++ b/plugin/rt/src/lib.rs @@ -4,6 +4,8 @@ pub extern crate plugin_derive; pub mod retreive; +use std::convert::TryInto; + pub use retreive::*; pub use plugin_api as api; @@ -14,22 +16,22 @@ use serde::{de::DeserializeOwned, Serialize}; #[cfg(target_arch = "wasm32")] extern "C" { 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) -> (i32, u32); } -pub fn retreive_action(_actions: &api::Retreive) -> Result { - #[cfg(target_arch = "wasm32")] - { - let ret = bincode::serialize(&actions).expect("Can't serialize action in emit"); - unsafe { - let (ptr,len) = raw_retreive_action(ret.as_ptr(), ret.len()); - let a = ::std::slice::from_raw_parts(ptr as _, len as _); - bincode::deserialize(&a) - } - } - #[cfg(not(target_arch = "wasm32"))] - unreachable!() -} +// pub fn retreive_action(_actions: &api::Retreive) -> Result { +// #[cfg(target_arch = "wasm32")] +// { +// let ret = bincode::serialize(&_actions).expect("Can't serialize action in emit"); +// unsafe { +// let (ptr,len) = raw_retreive_action(ret.as_ptr(), ret.len()); +// let a = ::std::slice::from_raw_parts(ptr as _, len as _); +// bincode::deserialize(&a) +// } +// } +// #[cfg(not(target_arch = "wasm32"))] +// unreachable!() +// } pub fn emit_action(action: api::Action) { emit_actions(vec![action]) } @@ -51,9 +53,15 @@ where bincode::deserialize(slice).map_err(|_| "Failed to deserialize function input") } -pub fn write_output(value: impl Serialize) -> (i32,i32) { +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]]) +} + +pub fn write_output(value: impl Serialize) -> i64 { let ret = bincode::serialize(&value).expect("Can't serialize event output"); - (ret.as_ptr() as _, ret.len() as _) + to_i64(ret.as_ptr() as _, ret.len() as _) } static mut BUFFERS: Vec = Vec::new(); diff --git a/plugin/rt/src/retreive.rs b/plugin/rt/src/retreive.rs index 049f32e999..1a805307ed 100644 --- a/plugin/rt/src/retreive.rs +++ b/plugin/rt/src/retreive.rs @@ -7,6 +7,7 @@ trait GetEntityName { impl GetEntityName for crate::api::event::Player { fn get_entity_name(&self) -> String { - crate::retreive_action(&Retreive::GetEntityName(self.id)).expect("Can't get entity name") + // crate::retreive_action(&Retreive::GetEntityName(self.id)).expect("Can't get entity name") + String::new() } } \ No newline at end of file diff --git a/server/src/lib.rs b/server/src/lib.rs index 2f4d792e72..c45175d2a8 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1037,12 +1037,12 @@ impl Server { command: kwd.clone(), command_args: args.split(' ').map(|x| x.to_owned()).collect(), player: plugin_api::event::Player { - id: *(self + id: plugin_api::Uid((self .state .ecs() .read_storage::() .get(entity) - .expect("Can't get player UUID [This should never appen]")), + .expect("Can't get player UUID [This should never appen]")).0), }, }, );