Fixed the plugin compilation and added comments

TODO: Remove compilation errors on WASM side (while using common).
This commit is contained in:
ccgauche 2021-02-16 20:52:38 +01:00 committed by Marcel Märtens
parent 8a693d9fde
commit 1597fcc79e
10 changed files with 93 additions and 35 deletions

1
Cargo.lock generated
View File

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

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

Binary file not shown.

View File

@ -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<T: Event> PreparedEventQuery<T> {
}
}
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<Vec<u8>, 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<Action>) {

View File

@ -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!(

View File

@ -5,5 +5,4 @@ authors = ["ccgauche <gaucheron.laurent@gmail.com>"]
edition = "2018"
[dependencies]
common = { package = "veloren-common", path = "../../common", features = ["no-assets"] }
serde = { version = "1.0.118", features = ["derive"] }

View File

@ -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<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 {
use super::*;

View File

@ -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

View File

@ -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<T: DeserializeOwned>(_actions: &api::Retreive) -> Result<T,bincode::Error> {
#[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<T: DeserializeOwned>(_actions: &api::Retreive) -> Result<T,bincode::Error> {
// #[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<u8> = Vec::new();

View File

@ -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()
}
}

View File

@ -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::<Uid>()
.get(entity)
.expect("Can't get player UUID [This should never appen]")),
.expect("Can't get player UUID [This should never appen]")).0),
},
},
);