Testing preliminary plugin loading through network

This commit is contained in:
ccgauche 2021-01-10 21:58:17 +01:00
parent 1604616768
commit 1dfe4af916
6 changed files with 270 additions and 38 deletions

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

Binary file not shown.

View File

@ -41,7 +41,7 @@ use common_net::{
},
sync::WorldSyncExt,
};
use common_sys::state::State;
use common_sys::{plugin::PluginMgr, state::State};
use comp::BuffKind;
use futures_executor::block_on;
use futures_timer::Delay;
@ -218,6 +218,7 @@ impl Client {
ServerInit::GameSync {
entity_package,
time_of_day,
plugins,
max_group_size,
client_timeout,
world_map,
@ -372,6 +373,11 @@ impl Client {
let map_bounds = Vec2::new(sea_level, max_height);
debug!("Done preparing image...");
#[cfg(feature = "plugins")]
if let Some(e) = plugins {
state.ecs_mut().write_resource::<PluginMgr>().load_server_plugins(&e);
}
Ok((
state,
entity,
@ -382,7 +388,7 @@ impl Client {
world_map.sites,
recipe_book,
max_group_size,
client_timeout,
client_timeout
))
},
ServerInit::TooManyPlayers => Err(Error::TooManyPlayers),

View File

@ -55,6 +55,7 @@ pub enum ServerInit {
client_timeout: Duration,
world_map: crate::msg::world_msg::WorldMapMsg,
recipe_book: RecipeBook,
plugins: Option<Vec<(String,Vec<Vec<u8>>)>>,
ability_map: comp::item::tool::AbilityMap,
},
}

View File

@ -20,21 +20,52 @@ use self::{
use rayon::prelude::*;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum PluginEnvironement {
LOCAL,
DISTANT,
BOTH
}
impl PluginEnvironement {
pub fn is_local(&self) -> bool {
matches!(self, Self::LOCAL | Self::BOTH)
}
pub fn is_distant(&self) -> bool {
matches!(self, Self::DISTANT | Self::BOTH)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PluginData {
name: String,
target: PluginEnvironement,
modules: HashSet<PathBuf>,
dependencies: HashSet<String>,
}
#[derive(Clone)]
pub struct Plugin {
data: PluginData,
modules: Vec<PluginModule>,
files: HashMap<PathBuf, Vec<u8>>,
pub enum PluginFile {
ForDistant {
data: PluginData,
bytes: Vec<Vec<u8>>,
},
ForLocal {
data: PluginData,
modules: Vec<PluginModule>,
files: HashMap<PathBuf, Vec<u8>>,
},
Both {
data: PluginData,
modules: Vec<PluginModule>,
files: HashMap<PathBuf, Vec<u8>>,
bytes: Vec<Vec<u8>>,
}
}
impl Plugin {
impl PluginFile {
pub fn from_reader<R: Read>(mut reader: R) -> Result<Self, PluginError> {
let mut buf = Vec::new();
reader.read_to_end(&mut buf).map_err(PluginError::Io)?;
@ -60,25 +91,65 @@ impl Plugin {
)
.map_err(PluginError::Toml)?;
let modules = data
.modules
.iter()
.map(|path| {
let wasm_data = files.remove(path).ok_or(PluginError::NoSuchModule)?;
PluginModule::new(data.name.to_owned(), &wasm_data).map_err(|e| {
PluginError::PluginModuleError(data.name.to_owned(), "<init>".to_owned(), e)
})
})
.collect::<Result<_, _>>()?;
Ok(Plugin {
data,
modules,
files,
Ok(match (data.target.is_local(),data.target.is_distant()) {
(true,e) => {
let mut bytes = Vec::new();
let modules = data
.modules
.iter()
.map(|path| {
let wasm_data = files.remove(path).ok_or(PluginError::NoSuchModule)?;
let tmp = PluginModule::new(data.name.to_owned(), &wasm_data).map_err(|e| {
PluginError::PluginModuleError(data.name.to_owned(), "<init>".to_owned(), e)
});
bytes.push(wasm_data);
tmp
})
.collect::<Result<_, _>>()?;
if e {
Self::Both {
data,
modules,
files,
bytes,
}
} else {
Self::ForLocal {
data,
modules,
files,
}
}
},
(false,_) => {
let bytes = data
.modules
.iter()
.map(|path| {
files.remove(path).ok_or(PluginError::NoSuchModule)
})
.collect::<Result<_, _>>()?;
Self::ForDistant {
data,
bytes,
}
}
})
}
pub fn execute_prepared<T>(
pub fn get_data(&self) -> &PluginData {
// Wait for let-or syntax to be stable
match self {
Self::ForLocal {data,..} | Self::Both {data,..} | Self::ForDistant {data,..} => data
}
}
}
impl PluginExecutable for PluginFile {
fn execute_prepared<T>(
&self,
event_name: &str,
event: &PreparedEventQuery<T>,
@ -86,13 +157,14 @@ impl Plugin {
where
T: Event,
{
self.modules
if let Self::ForLocal {modules,data,..} | Self::Both {modules,data,..} = self {
modules
.iter()
.flat_map(|module| {
module.try_execute(event_name, event).map(|x| {
x.map_err(|e| {
PluginError::PluginModuleError(
self.data.name.to_owned(),
data.name.to_owned(),
event_name.to_owned(),
e,
)
@ -100,22 +172,68 @@ impl Plugin {
})
})
.collect::<Result<Vec<_>, _>>()
} else {
Ok(Vec::new())
}
}
fn get_name(&self) -> &str {
&self.get_data().name
}
}
#[derive(Clone, Default)]
pub struct PluginMgr {
plugins: Vec<Plugin>,
plugins: Vec<PluginFile>,
plugins_from_server: Vec<BinaryPlugin>
}
impl PluginMgr {
pub fn from_assets() -> Result<Self, PluginError> {
println!("NEW PLUGIN MGR");
let mut assets_path = (&*ASSETS_PATH).clone();
assets_path.push("plugins");
info!("Searching {:?} for plugins...", assets_path);
Self::from_dir(assets_path)
}
pub fn load_server_plugins(&mut self, plugins: &Vec<(String,Vec<Vec<u8>>)>) {
let prepared = PreparedEventQuery::new(&plugin_api::event::PluginLoadEvent { game_mode: plugin_api::GameMode::Client }).unwrap();
self.plugins_from_server.extend(plugins.iter().flat_map(|(name,bytes)| {
info!("Loading {} with {} module(s) from server",name,bytes.len());
match BinaryPlugin::from_bytes(name.clone(), bytes) {
Ok(e) => {
if let Err(e) = e.modules.iter().flat_map(|x| {
x.try_execute("on_load", &prepared)
}).collect::<Result<Vec<_>, _>>() {
error!("Error while executing `on_load` on network retreived plugin: `{}` \n{:?}",name,e);
}
Some(e)
},
Err(e) => {
tracing::error!("Error while loading distant plugin! Contact the server administrator!\n{:?}",e);
None
}
}
}));
println!("Plugins from server: {}",self.plugins_from_server.len());
}
pub fn clear_server_plugins(&mut self) {
self.plugins_from_server.clear();
}
pub fn get_module_bytes(&self) -> Vec<(String,Vec<Vec<u8>>)> {
self.plugins.iter().flat_map(|x| {
if let PluginFile::ForDistant {data, bytes, ..} |
PluginFile::Both {data, bytes, ..} = x {
Some((data.name.clone(), bytes.clone()))
} else {
None
}
}).collect()
}
pub fn execute_prepared<T>(
&self,
event_name: &str,
@ -124,14 +242,27 @@ impl PluginMgr {
where
T: Event,
{
Ok(self
println!("{}",event_name);
let mut o = self
.plugins
.par_iter()
.map(|plugin| plugin.execute_prepared(event_name, event))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect())
.collect::<Vec<_>>();
println!("Event exe 2 {}",self.plugins_from_server.len());
o.extend(self
.plugins_from_server
.par_iter()
.map(|plugin| {
println!("Event exe 3");
plugin.execute_prepared(event_name, event)
})
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten());
Ok(o)
}
pub fn execute_event<T>(
@ -142,6 +273,8 @@ impl PluginMgr {
where
T: Event,
{
println!("Event exe 1");
self.execute_prepared(event_name, &PreparedEventQuery::new(event)?)
}
@ -159,7 +292,7 @@ impl PluginMgr {
.unwrap_or(false)
{
info!("Loading plugin at {:?}", entry.path());
Plugin::from_reader(fs::File::open(entry.path()).map_err(PluginError::Io)?)
PluginFile::from_reader(fs::File::open(entry.path()).map_err(PluginError::Io)?)
.map(Some)
} else {
Ok(None)
@ -172,13 +305,91 @@ impl PluginMgr {
.collect::<Result<Vec<_>, _>>()?;
for plugin in &plugins {
info!(
"Loaded plugin '{}' with {} module(s)",
plugin.data.name,
plugin.modules.len()
);
match plugin {
PluginFile::Both { data, modules, .. } | PluginFile::ForLocal { data, modules, .. } => {
info!(
"Loaded plugin '{}' with {} module(s)",
data.name,
modules.len()
);
}
PluginFile::ForDistant { data, bytes } => {
info!(
"Loaded plugin '{}' with {} module(s)",
data.name,
bytes.len()
);
}
}
}
Ok(Self { plugins })
Ok(Self { plugins, plugins_from_server: Vec::new() })
}
}
#[derive(Clone)]
pub struct BinaryPlugin {
modules: Vec<PluginModule>,
name: String,
}
impl BinaryPlugin {
pub fn from_bytes(name: String, bytes: &Vec<Vec<u8>>) -> Result<Self, PluginError> {
Ok(
Self {
modules: bytes.iter().enumerate().map(|(i,module)| {
PluginModule::new(format!("{}-module({})",name,i), module).map_err(|e| {
PluginError::PluginModuleError(name.clone(), "<init>".to_owned(), e)
})
}).collect::<Result<Vec<_>,_>>()?,
name
}
)
}
}
impl PluginExecutable for BinaryPlugin {
fn execute_prepared<T>(
&self,
event_name: &str,
event: &PreparedEventQuery<T>,
) -> Result<Vec<T::Response>, PluginError>
where
T: Event,
{
println!("Event launched");
self.modules
.iter()
.flat_map(|module| {
println!("1: {}",event_name);
module.try_execute(event_name, event).map(|x| {
x.map_err(|e| {
PluginError::PluginModuleError(
self.name.to_owned(),
event_name.to_owned(),
e,
)
})
})
})
.collect::<Result<Vec<_>, _>>()
}
fn get_name(&self) -> &str {
&self.name
}
}
pub trait PluginExecutable {
fn execute_prepared<T>(
&self,
event_name: &str,
event: &PreparedEventQuery<T>,
) -> Result<Vec<T::Response>, PluginError>
where
T: Event;
fn get_name(&self) -> &str;
}

View File

@ -7,16 +7,20 @@ pub use plugin_derive::*;
use serde::{de::DeserializeOwned, Serialize};
#[cfg(target_arch = "wasm32")]
extern "C" {
fn raw_emit_actions(ptr: *const u8, len: usize);
}
pub fn emit_action(action: api::Action) { emit_actions(vec![action]) }
pub fn emit_actions(actions: Vec<api::Action>) {
let ret = bincode::serialize(&actions).expect("Can't serialize action in emit");
unsafe {
raw_emit_actions(ret.as_ptr(), ret.len());
pub fn emit_actions(_actions: Vec<api::Action>) {
#[cfg(target_arch = "wasm32")]
{
let ret = bincode::serialize(&_actions).expect("Can't serialize action in emit");
unsafe {
raw_emit_actions(ret.as_ptr(), ret.len());
}
}
}

View File

@ -940,6 +940,13 @@ impl Server {
client_timeout: self.settings().client_timeout,
world_map: self.map.clone(),
recipe_book: default_recipe_book().cloned(),
#[cfg(feature = "plugins")]
plugins: Some(self
.state
.ecs()
.read_resource::<PluginMgr>().get_module_bytes()),
#[cfg(not(feature = "plugins"))]
plugins: None,
ability_map: (&*self
.state
.ecs()