Added raw_print API, log macro to plugin API

This commit is contained in:
Joshua Barretto 2021-06-11 22:23:58 +01:00
parent 47b522e156
commit 51e5b39f5c
6 changed files with 89 additions and 40 deletions

1
assets/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
plugins/*

View File

@ -228,13 +228,19 @@ pub fn read_data<T: DeserializeOwned>(
position: u64,
length: u64,
) -> Result<T, bincode::Error> {
bincode::deserialize(&read_bytes(memory, position, length))
bincode::deserialize(
&read_bytes(memory, position, length).ok_or(bincode::ErrorKind::SizeLimit)?,
)
}
/// This function read raw bytes from memory at a position with the array length
pub fn read_bytes(memory: &Memory, position: u64, length: u64) -> Vec<u8> {
memory.view()[(position as usize)..(position as usize) + length as usize]
.iter()
.map(|x| x.get())
.collect()
pub fn read_bytes(memory: &Memory, position: u64, length: u64) -> Option<Vec<u8>> {
Some(
memory
.view()
.get((position as usize)..(position as usize) + length as usize)?
.iter()
.map(|x| x.get())
.collect(),
)
}

View File

@ -1,5 +1,6 @@
use hashbrown::HashSet;
use std::{
collections::HashSet,
borrow::Cow,
convert::TryInto,
marker::PhantomData,
sync::{Arc, Mutex},
@ -11,7 +12,7 @@ use wasmer::{imports, Cranelift, Function, Instance, Memory, Module, Store, Valu
use super::{
errors::{PluginError, PluginModuleError},
memory_manager::{self, EcsAccessManager, EcsWorld, MemoryManager},
wasm_env::HostFunctionEnvironement,
wasm_env::HostFunctionEnvironment,
};
use plugin_api::{Action, EcsAccessError, Event, Retrieve, RetrieveError, RetrieveResult};
@ -39,7 +40,7 @@ impl PluginModule {
let module = Module::new(&store, &wasm_data).expect("Can't compile");
// This is the function imported into the wasm environement
fn raw_emit_actions(env: &HostFunctionEnvironement, ptr: i64, len: i64) {
fn raw_emit_actions(env: &HostFunctionEnvironment, ptr: i64, len: i64) {
handle_actions(match env.read_data(from_i64(ptr), from_i64(len)) {
Ok(e) => e,
Err(e) => {
@ -49,7 +50,7 @@ impl PluginModule {
});
}
fn raw_retrieve_action(env: &HostFunctionEnvironement, ptr: i64, len: i64) -> i64 {
fn raw_retrieve_action(env: &HostFunctionEnvironment, ptr: i64, len: i64) -> i64 {
let out = match env.read_data(from_i64(ptr), from_i64(len)) {
Ok(data) => retrieve_action(&env.ecs, data),
Err(e) => Err(RetrieveError::BincodeError(e.to_string())),
@ -60,8 +61,15 @@ impl PluginModule {
to_i64(env.write_data_as_pointer(&out).unwrap())
}
fn dbg(a: i32) {
println!("WASM DEBUG: {}", a);
fn raw_print(env: &HostFunctionEnvironment, ptr: i64, len: i64) {
if let Some(msg) = env
.read_bytes(from_i64(ptr), from_i64(len))
.and_then(|bytes| String::from_utf8(bytes).ok())
{
tracing::info!("[{}] {}", env.name, msg);
} else {
tracing::error!("Logging message from plugin {} failed!", env.name);
}
}
let ecs = Arc::new(EcsAccessManager::default());
@ -70,9 +78,9 @@ impl PluginModule {
// Create an import object.
let import_object = imports! {
"env" => {
"raw_emit_actions" => Function::new_native_with_env(&store, HostFunctionEnvironement::new(name.clone(), ecs.clone(),memory_manager.clone()), raw_emit_actions),
"raw_retrieve_action" => Function::new_native_with_env(&store, HostFunctionEnvironement::new(name.clone(), ecs.clone(),memory_manager.clone()), raw_retrieve_action),
"dbg" => Function::new_native(&store, dbg),
"raw_emit_actions" => Function::new_native_with_env(&store, HostFunctionEnvironment::new(name.clone(), ecs.clone(), memory_manager.clone()), raw_emit_actions),
"raw_retrieve_action" => Function::new_native_with_env(&store, HostFunctionEnvironment::new(name.clone(), ecs.clone(), memory_manager.clone()), raw_retrieve_action),
"raw_print" => Function::new_native_with_env(&store, HostFunctionEnvironment::new(name.clone(), ecs.clone(), memory_manager.clone()), raw_print),
}
};
@ -112,7 +120,7 @@ impl PluginModule {
where
T: Event,
{
if !self.events.contains(&request.function_name) {
if !self.events.contains(request.function_name.as_ref()) {
return None;
}
// Store the ECS Pointer for later use in `retreives`
@ -131,7 +139,7 @@ impl PluginModule {
/// reencoding for each module in every plugin)
pub struct PreparedEventQuery<T> {
bytes: Vec<u8>,
function_name: String,
function_name: Cow<'static, str>,
_phantom: PhantomData<T>,
}
@ -214,17 +222,20 @@ fn execute_raw(
.ok_or_else(PluginModuleError::InvalidArgumentType)?,
);
let bytes = memory_manager::read_bytes(&module.memory, u128_pointer, 16);
// TODO: Codify error
let bytes = memory_manager::read_bytes(&module.memory, u128_pointer, 16).unwrap();
// We read the return object and deserialize it
// The first 8 bytes are encoded as le and represent the pointer to the data
// The next 8 bytes are encoded as le and represent the length of the data
// TODO: Codify error
Ok(memory_manager::read_bytes(
&module.memory,
u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
))
)
.unwrap())
}
fn retrieve_action(

View File

@ -9,7 +9,7 @@ use super::{
};
#[derive(Clone)]
pub struct HostFunctionEnvironement {
pub struct HostFunctionEnvironment {
pub ecs: Arc<EcsAccessManager>, /* This represent the pointer to the ECS object (set to
* i32::MAX if to ECS is
* availible) */
@ -20,7 +20,7 @@ pub struct HostFunctionEnvironement {
pub name: String, // This represent the plugin name
}
impl HostFunctionEnvironement {
impl HostFunctionEnvironment {
pub fn new(
name: String,
ecs: Arc<EcsAccessManager>,
@ -67,9 +67,15 @@ impl HostFunctionEnvironement {
) -> Result<T, bincode::Error> {
memory_manager::read_data(self.memory.get_ref().unwrap(), position, length)
}
/// This function is a safe interface to WASM memory that reads memory from
/// pointer and length returning an object
pub fn read_bytes(&self, position: u64, length: u64) -> Option<Vec<u8>> {
memory_manager::read_bytes(self.memory.get_ref().unwrap(), position, length)
}
}
impl WasmerEnv for HostFunctionEnvironement {
impl WasmerEnv for HostFunctionEnvironment {
fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> {
let memory = instance.exports.get_memory("memory").unwrap();
self.memory.initialize(memory.clone());

View File

@ -1,9 +1,8 @@
pub extern crate common;
//#![deny(missing_docs)]
pub use common::comp::Health;
pub use common::{comp::Health, resources::GameMode, uid::Uid};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub use common::{resources::GameMode, uid::Uid};
use std::borrow::Cow;
mod errors;
@ -97,7 +96,7 @@ pub enum RetrieveResult {
pub trait Event: Serialize + DeserializeOwned + Send + Sync {
type Response: Serialize + DeserializeOwned + Send + Sync;
fn get_event_name(&self) -> String;
fn get_event_name(&self) -> Cow<'static, str>;
}
/// This module contains all events from the api
@ -141,7 +140,9 @@ pub mod event {
impl Event for ChatCommandEvent {
type Response = Result<Vec<String>, String>;
fn get_event_name(&self) -> String { format!("on_command_{}", self.command) }
fn get_event_name(&self) -> Cow<'static, str> {
Cow::Owned(format!("on_command_{}", self.command))
}
}
/// This struct represent a player
@ -172,7 +173,7 @@ pub mod event {
impl Event for PlayerJoinEvent {
type Response = PlayerJoinResult;
fn get_event_name(&self) -> String { "on_join".to_owned() }
fn get_event_name(&self) -> Cow<'static, str> { Cow::Borrowed("on_join") }
}
/// This is the return type of an `on_join` event. See [`PlayerJoinEvent`]
@ -213,7 +214,7 @@ pub mod event {
impl Event for PluginLoadEvent {
type Response = ();
fn get_event_name(&self) -> String { "on_load".to_owned() }
fn get_event_name(&self) -> Cow<'static, str> { Cow::Borrowed("on_load") }
}
// impl Default for PlayerJoinResult {

View File

@ -1,24 +1,35 @@
pub extern crate plugin_derive;
extern crate plugin_derive;
pub mod retrieve;
use api::RetrieveError;
pub use retrieve::*;
use std::convert::TryInto;
pub use retrieve::*;
pub use plugin_api as api;
pub use plugin_derive::*;
pub use plugin_derive::{event_handler, global_state};
pub use retrieve::*;
use api::RetrieveError;
use serde::{de::DeserializeOwned, Serialize};
use std::{convert::TryInto, marker::PhantomData};
pub struct Game {
phantom: PhantomData<()>,
}
impl Game {
/// This is not strictly unsafe today, but it may become unsafe in the
/// future. No safety guarantees are listed because we don't intend this
/// to ever be manually called. Do not use this function!
pub unsafe fn __new() -> Self {
Self {
phantom: PhantomData,
}
}
}
#[cfg(target_arch = "wasm32")]
extern "C" {
fn raw_emit_actions(ptr: i64, len: i64);
fn raw_retrieve_action(ptr: i64, len: i64) -> i64;
pub fn dbg(i: i32);
fn raw_print(ptr: i64, len: i64);
}
pub fn retrieve_action<T: DeserializeOwned>(_actions: &api::Retrieve) -> Result<T, RetrieveError> {
@ -51,6 +62,19 @@ pub fn emit_actions(_actions: Vec<api::Action>) {
}
}
pub fn print_str(s: &str) {
let bytes = s.as_bytes();
unsafe {
// Safety: ptr and len are valid for byte slice
raw_print(to_i64(bytes.as_ptr() as _), to_i64(bytes.len() as _));
}
}
#[macro_export]
macro_rules! log {
($($x:tt)*) => { $crate::print_str(&format!($($x)*)) };
}
pub fn read_input<T>(ptr: i64, len: i64) -> Result<T, &'static str>
where
T: DeserializeOwned,