2020-12-13 12:27:48 +00:00
|
|
|
use std::{
|
|
|
|
cell::Cell,
|
|
|
|
collections::HashSet,
|
|
|
|
marker::PhantomData,
|
|
|
|
sync::{Arc, Mutex},
|
|
|
|
};
|
2020-12-11 23:37:22 +00:00
|
|
|
|
|
|
|
use error::RuntimeError;
|
|
|
|
use wasmer_runtime::*;
|
|
|
|
|
2020-12-14 16:07:05 +00:00
|
|
|
use super::errors::{MemoryAllocationError, PluginError, PluginModuleError};
|
2020-12-12 13:01:54 +00:00
|
|
|
use plugin_api::{Action, Event};
|
2020-12-11 23:37:22 +00:00
|
|
|
|
|
|
|
// This represent a WASM function interface
|
|
|
|
pub type Function<'a> = Func<'a, (i32, u32), i32>;
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2020-12-12 00:19:20 +00:00
|
|
|
// This structure represent the WASM State of the plugin.
|
2020-12-11 23:37:22 +00:00
|
|
|
pub struct PluginModule {
|
|
|
|
wasm_instance: Arc<Mutex<Instance>>,
|
2020-12-12 00:19:20 +00:00
|
|
|
events: HashSet<String>,
|
2020-12-11 23:37:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PluginModule {
|
2020-12-12 00:19:20 +00:00
|
|
|
// This function takes bytes from a WASM File and compile them
|
2020-12-13 17:40:15 +00:00
|
|
|
pub fn new(wasm_data: &[u8]) -> Result<Self, PluginModuleError> {
|
|
|
|
let module = compile(&wasm_data).map_err(PluginModuleError::Compile)?;
|
2020-12-11 23:37:22 +00:00
|
|
|
let instance = module
|
|
|
|
.instantiate(&imports! {"env" => {
|
2020-12-12 19:26:42 +00:00
|
|
|
"raw_emit_actions" => func!(read_action),
|
2020-12-13 17:40:15 +00:00
|
|
|
}})
|
|
|
|
.map_err(PluginModuleError::Instantiate)?;
|
2020-12-12 13:01:54 +00:00
|
|
|
|
2020-12-11 23:37:22 +00:00
|
|
|
Ok(Self {
|
|
|
|
events: instance.exports.into_iter().map(|(name, _)| name).collect(),
|
|
|
|
wasm_instance: Arc::new(Mutex::new(instance)),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-12-13 17:40:15 +00:00
|
|
|
// This function tries to execute an event for the current module. Will return
|
|
|
|
// None if the event doesn't exists
|
2020-12-11 23:37:22 +00:00
|
|
|
pub fn try_execute<T>(
|
|
|
|
&self,
|
|
|
|
event_name: &str,
|
|
|
|
request: &PreparedEventQuery<T>,
|
2020-12-13 17:40:15 +00:00
|
|
|
) -> Option<Result<T::Response, PluginModuleError>>
|
2020-12-11 23:37:22 +00:00
|
|
|
where
|
|
|
|
T: Event,
|
|
|
|
{
|
2020-12-12 00:19:20 +00:00
|
|
|
if !self.events.contains(event_name) {
|
2020-12-11 23:37:22 +00:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let bytes = {
|
2020-12-13 12:27:48 +00:00
|
|
|
let instance = self.wasm_instance.lock().unwrap();
|
2020-12-13 17:40:15 +00:00
|
|
|
let func = match instance
|
|
|
|
.exports
|
|
|
|
.get(event_name)
|
|
|
|
.map_err(PluginModuleError::FunctionGet)
|
|
|
|
{
|
2020-12-11 23:37:22 +00:00
|
|
|
Ok(e) => e,
|
2020-12-13 17:40:15 +00:00
|
|
|
Err(e) => return Some(Err(e)),
|
2020-12-11 23:37:22 +00:00
|
|
|
};
|
2020-12-14 16:07:05 +00:00
|
|
|
match execute_raw(&instance, &func, &request.bytes).map_err(PluginModuleError::RunFunction) {
|
2020-12-11 23:37:22 +00:00
|
|
|
Ok(e) => e,
|
2020-12-13 17:40:15 +00:00
|
|
|
Err(e) => return Some(Err(e)),
|
2020-12-11 23:37:22 +00:00
|
|
|
}
|
|
|
|
};
|
2020-12-13 17:40:15 +00:00
|
|
|
Some(bincode::deserialize(&bytes).map_err(PluginModuleError::Encoding))
|
2020-12-11 23:37:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-13 17:40:15 +00:00
|
|
|
// This structure represent a Pre-encoded event object (Useful to avoid
|
|
|
|
// reencoding for each module in every plugin)
|
2020-12-11 23:37:22 +00:00
|
|
|
pub struct PreparedEventQuery<T> {
|
|
|
|
bytes: Vec<u8>,
|
|
|
|
_phantom: PhantomData<T>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Event> PreparedEventQuery<T> {
|
2020-12-12 00:19:20 +00:00
|
|
|
// Create a prepared query from an event reference (Encode to bytes the struct)
|
2020-12-11 23:37:22 +00:00
|
|
|
// This Prepared Query is used by the `try_execute` method in `PluginModule`
|
|
|
|
pub fn new(event: &T) -> Result<Self, PluginError>
|
|
|
|
where
|
|
|
|
T: Event,
|
|
|
|
{
|
|
|
|
Ok(Self {
|
2020-12-13 17:40:15 +00:00
|
|
|
bytes: bincode::serialize(&event)
|
|
|
|
.map_err(|e| PluginError::PluginModuleError(PluginModuleError::Encoding(e)))?,
|
2020-12-11 23:37:22 +00:00
|
|
|
_phantom: PhantomData::default(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-13 17:40:15 +00:00
|
|
|
// This function is not public because this function should not be used without
|
|
|
|
// an interface to limit unsafe behaviours
|
|
|
|
#[allow(clippy::needless_range_loop)]
|
2020-12-11 23:37:22 +00:00
|
|
|
fn execute_raw(
|
2020-12-14 16:07:05 +00:00
|
|
|
instance: &Instance,
|
2020-12-11 23:37:22 +00:00
|
|
|
function: &Function,
|
|
|
|
bytes: &[u8],
|
|
|
|
) -> Result<Vec<u8>, RuntimeError> {
|
2020-12-14 16:07:05 +00:00
|
|
|
// This reserves space for the buffer
|
2020-12-11 23:37:22 +00:00
|
|
|
let len = bytes.len();
|
2020-12-14 16:07:05 +00:00
|
|
|
let start = {
|
|
|
|
let memory_pos = reserve_wasm_memory_buffer(len,instance).expect("Fatal error while allocating memory for a plugin! Closing server...") as usize;
|
|
|
|
let memory = instance.context().memory(0);
|
|
|
|
let view = memory.view::<u8>();
|
|
|
|
for (cell, byte) in view[memory_pos..memory_pos+len].iter().zip(bytes.iter()) {
|
|
|
|
cell.set(*byte)
|
|
|
|
}
|
|
|
|
function.call(memory_pos as i32, len as u32)? as usize
|
|
|
|
};
|
|
|
|
|
|
|
|
let memory = instance.context().memory(0);
|
2020-12-11 23:37:22 +00:00
|
|
|
let view = memory.view::<u8>();
|
|
|
|
let mut new_len_bytes = [0u8; 4];
|
|
|
|
// TODO: It is probably better to dirrectly make the new_len_bytes
|
|
|
|
for i in 0..4 {
|
2020-12-12 11:40:45 +00:00
|
|
|
new_len_bytes[i] = view.get(i + 1).map(Cell::get).unwrap_or(0);
|
2020-12-11 23:37:22 +00:00
|
|
|
}
|
|
|
|
let new_len = u32::from_ne_bytes(new_len_bytes) as usize;
|
|
|
|
Ok(view[start..start + new_len]
|
|
|
|
.iter()
|
|
|
|
.map(|c| c.get())
|
|
|
|
.collect())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read_action(ctx: &mut Ctx, ptr: u32, len: u32) {
|
|
|
|
let memory = ctx.memory(0);
|
|
|
|
|
|
|
|
let memory = memory.view::<u8>();
|
|
|
|
|
|
|
|
let str_slice = &memory[ptr as usize..(ptr + len) as usize];
|
|
|
|
|
|
|
|
let bytes: Vec<u8> = str_slice.iter().map(|x| x.get()).collect();
|
|
|
|
|
|
|
|
let e: Vec<Action> = match bincode::deserialize(&bytes) {
|
|
|
|
Ok(e) => e,
|
|
|
|
Err(e) => {
|
|
|
|
tracing::error!(?e, "Can't decode action");
|
|
|
|
return;
|
2020-12-13 17:40:15 +00:00
|
|
|
},
|
2020-12-11 23:37:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
for action in e {
|
|
|
|
match action {
|
|
|
|
Action::ServerClose => {
|
|
|
|
tracing::info!("Server closed by plugin");
|
|
|
|
std::process::exit(-1);
|
2020-12-13 17:40:15 +00:00
|
|
|
},
|
2020-12-11 23:37:22 +00:00
|
|
|
Action::Print(e) => {
|
2020-12-13 17:40:15 +00:00
|
|
|
tracing::info!("{}", e);
|
|
|
|
},
|
2020-12-11 23:37:22 +00:00
|
|
|
Action::PlayerSendMessage(a, b) => {
|
2020-12-13 17:40:15 +00:00
|
|
|
tracing::info!("SendMessage {} -> {}", a, b);
|
|
|
|
},
|
2020-12-11 23:37:22 +00:00
|
|
|
Action::KillEntity(e) => {
|
2020-12-13 17:40:15 +00:00
|
|
|
tracing::info!("Kill Entity {}", e);
|
|
|
|
},
|
2020-12-11 23:37:22 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-12 13:01:54 +00:00
|
|
|
}
|
2020-12-14 16:07:05 +00:00
|
|
|
|
|
|
|
fn reserve_wasm_memory_buffer<'a>(
|
|
|
|
value: usize,
|
|
|
|
instance: &'a Instance,
|
|
|
|
) -> Result<i32, MemoryAllocationError> {
|
|
|
|
instance
|
|
|
|
.exports
|
|
|
|
.get::<Func<'a, i32, i32>>("wasm_prepare_buffer")
|
|
|
|
.map_err(|e| MemoryAllocationError::AllocatorNotFound(e))?
|
|
|
|
.call(value as i32)
|
|
|
|
.map_err(|e| MemoryAllocationError::CantAllocate(e))
|
|
|
|
}
|