mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Testing preliminary plugin loading through network
This commit is contained in:
parent
1604616768
commit
1dfe4af916
BIN
assets/plugins/plugin1.plugin.tar
(Stored with Git LFS)
Normal file
BIN
assets/plugins/plugin1.plugin.tar
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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),
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user