mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Began work on plugin API and plugin loading
This commit is contained in:
parent
fc7a3748c0
commit
12b29ea174
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -3973,7 +3973,7 @@ dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"spin 0.5.2",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi 0.3.9",
|
||||
@ -4495,6 +4495,12 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "652ac3743312871a5fb703f0337e68ffa3cdc28c863efad0b8dc858fa10c991b"
|
||||
|
||||
[[package]]
|
||||
name = "spin_sleep"
|
||||
version = "1.0.0"
|
||||
@ -4922,9 +4928,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.6"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
|
||||
checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -5346,11 +5352,21 @@ dependencies = [
|
||||
"spin_sleep",
|
||||
"structopt",
|
||||
"sum_type",
|
||||
"tar",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracy-client",
|
||||
"vek 0.12.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "veloren-plugin-api"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"spin 0.7.0",
|
||||
"veloren-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "veloren-server"
|
||||
version = "0.8.0"
|
||||
|
@ -5,6 +5,7 @@ members = [
|
||||
"common",
|
||||
"common/sys",
|
||||
"client",
|
||||
"plugin-api",
|
||||
"server",
|
||||
"server-cli",
|
||||
"voxygen",
|
||||
@ -75,6 +76,6 @@ inherits = 'release'
|
||||
debug = 1
|
||||
|
||||
[patch.crates-io]
|
||||
# macos CI fix isn't merged yet
|
||||
# macos CI fix isn't merged yet
|
||||
winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" }
|
||||
vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics" }
|
||||
|
BIN
assets/plugins/hello.plugin.tar
Normal file
BIN
assets/plugins/hello.plugin.tar
Normal file
Binary file not shown.
@ -34,6 +34,7 @@ directories-next = "2.0"
|
||||
dot_vox = "4.0"
|
||||
image = { version = "0.23.8", default-features = false, features = ["png"] }
|
||||
notify = "5.0.0-pre.3"
|
||||
tar = "0.4.30"
|
||||
|
||||
# Auth
|
||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "b943c85e4a38f5ec60cd18c34c73097640162bfe" }
|
||||
@ -53,6 +54,7 @@ ron = { version = "0.6", default-features = false }
|
||||
serde = { version = "1.0.110", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.50"
|
||||
serde_repr = "0.1.6"
|
||||
toml = "0.5.7"
|
||||
|
||||
#esv export
|
||||
csv = { version = "1.1.3", optional = true }
|
||||
|
@ -38,6 +38,7 @@ pub mod msg;
|
||||
pub mod npc;
|
||||
pub mod outcome;
|
||||
pub mod path;
|
||||
pub mod plugin;
|
||||
pub mod ray;
|
||||
pub mod recipe;
|
||||
pub mod region;
|
||||
|
130
common/src/plugin.rs
Normal file
130
common/src/plugin.rs
Normal file
@ -0,0 +1,130 @@
|
||||
use crate::assets::ASSETS_PATH;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
io::{self, Read},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tracing::{error, info};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PluginError {
|
||||
Io(io::Error),
|
||||
Toml(toml::de::Error),
|
||||
NoConfig,
|
||||
NoSuchModule,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct PluginData {
|
||||
name: String,
|
||||
modules: Vec<PathBuf>,
|
||||
dependencies: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PluginModule {
|
||||
wasm_data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Plugin {
|
||||
data: PluginData,
|
||||
modules: Vec<PluginModule>,
|
||||
files: HashMap<PathBuf, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Plugin {
|
||||
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)?;
|
||||
|
||||
let mut files = tar::Archive::new(&*buf)
|
||||
.entries()
|
||||
.map_err(PluginError::Io)?
|
||||
.map(|e| {
|
||||
e.and_then(|e| {
|
||||
Ok((e.path()?.into_owned(), {
|
||||
let offset = e.raw_file_position() as usize;
|
||||
buf[offset..offset + e.size() as usize].to_vec()
|
||||
}))
|
||||
})
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, _>>()
|
||||
.map_err(PluginError::Io)?;
|
||||
|
||||
let data = toml::de::from_slice::<PluginData>(
|
||||
&files
|
||||
.get(Path::new("plugin.toml"))
|
||||
.ok_or(PluginError::NoConfig)?,
|
||||
)
|
||||
.map_err(PluginError::Toml)?;
|
||||
|
||||
let modules = data
|
||||
.modules
|
||||
.iter()
|
||||
.map(|path| {
|
||||
let wasm_data = files.remove(path).ok_or(PluginError::NoSuchModule)?;
|
||||
Ok(PluginModule { wasm_data })
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(Plugin {
|
||||
data,
|
||||
modules,
|
||||
files,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PluginMgr {
|
||||
plugins: Vec<Plugin>,
|
||||
}
|
||||
|
||||
impl PluginMgr {
|
||||
pub fn from_assets() -> Result<Self, PluginError> {
|
||||
let mut assets_path = (&*ASSETS_PATH).clone();
|
||||
assets_path.push("plugins");
|
||||
info!("Searching {:?} for assets...", assets_path);
|
||||
Self::from_dir(assets_path)
|
||||
}
|
||||
|
||||
pub fn from_dir<P: AsRef<Path>>(path: P) -> Result<Self, PluginError> {
|
||||
let plugins = fs::read_dir(path)
|
||||
.map_err(PluginError::Io)?
|
||||
.filter_map(|e| e.ok())
|
||||
.map(|entry| {
|
||||
if entry.file_type().map(|ft| ft.is_file()).unwrap_or(false)
|
||||
&& entry
|
||||
.path()
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.map(|s| s.ends_with(".plugin.tar"))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
info!("Loading plugin at {:?}", entry.path());
|
||||
Plugin::from_reader(fs::File::open(entry.path()).map_err(PluginError::Io)?)
|
||||
.map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
.filter_map(Result::transpose)
|
||||
.inspect(|p| {
|
||||
let _ = p.as_ref().map_err(|e| error!(?e, "Failed to load plugin"));
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
for plugin in &plugins {
|
||||
info!(
|
||||
"Loaded plugin '{}' with {} module(s)",
|
||||
plugin.data.name,
|
||||
plugin.modules.len()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Self { plugins })
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ use common::{
|
||||
comp,
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
metrics::{PhysicsMetrics, SysMetrics},
|
||||
plugin::PluginMgr,
|
||||
region::RegionMap,
|
||||
resources::{DeltaTime, Time, TimeOfDay},
|
||||
span,
|
||||
@ -18,6 +19,7 @@ use specs::{
|
||||
Component, DispatcherBuilder, Entity as EcsEntity, WorldExt,
|
||||
};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use tracing::info;
|
||||
use vek::*;
|
||||
|
||||
/// How much faster should an in-game day be compared to a real day?
|
||||
@ -176,6 +178,15 @@ impl State {
|
||||
ecs.insert(SysMetrics::default());
|
||||
ecs.insert(PhysicsMetrics::default());
|
||||
|
||||
// Load plugins from asset directory
|
||||
ecs.insert(match PluginMgr::from_assets() {
|
||||
Ok(plugin_mgr) => plugin_mgr,
|
||||
Err(_) => {
|
||||
info!("Error occurred when loading plugins. Running without plugins instead.");
|
||||
PluginMgr::default()
|
||||
},
|
||||
});
|
||||
|
||||
ecs
|
||||
}
|
||||
|
||||
|
10
plugin-api/Cargo.toml
Normal file
10
plugin-api/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "veloren-plugin-api"
|
||||
version = "0.1.0"
|
||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
common = { package = "veloren-common", path = "../common" }
|
||||
|
||||
spin = "0.7"
|
2
plugin-api/hello/.gitignore
vendored
Normal file
2
plugin-api/hello/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Rust
|
||||
target
|
1647
plugin-api/hello/Cargo.lock
generated
Normal file
1647
plugin-api/hello/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
plugin-api/hello/Cargo.toml
Normal file
13
plugin-api/hello/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "hello"
|
||||
version = "0.1.0"
|
||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
plugin-api = { package = "veloren-plugin-api", path = ".." }
|
||||
|
||||
[workspace]
|
7
plugin-api/hello/src/lib.rs
Normal file
7
plugin-api/hello/src/lib.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use plugin_api::{PLUGIN, api};
|
||||
|
||||
pub extern fn main() {
|
||||
PLUGIN.on_start(|| {
|
||||
api::print("Hello from my plugin!");
|
||||
});
|
||||
}
|
7
plugin-api/src/api/mod.rs
Normal file
7
plugin-api/src/api/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use crate::raw_api;
|
||||
|
||||
pub fn print(s: &str) {
|
||||
unsafe {
|
||||
raw_api::print(s.as_bytes().as_ptr(), s.len());
|
||||
}
|
||||
}
|
36
plugin-api/src/lib.rs
Normal file
36
plugin-api/src/lib.rs
Normal file
@ -0,0 +1,36 @@
|
||||
#![feature(const_fn)]
|
||||
|
||||
pub mod api;
|
||||
pub mod raw_api;
|
||||
pub mod raw_hooks;
|
||||
|
||||
use spin::Mutex;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Hook {
|
||||
OnStart,
|
||||
OnTick,
|
||||
OnStop,
|
||||
}
|
||||
|
||||
pub struct Plugin {
|
||||
pub on_start: Mutex<Vec<Box<dyn Fn() + Send + Sync>>>,
|
||||
pub on_tick: Mutex<Vec<Box<dyn Fn() + Send + Sync>>>,
|
||||
pub on_stop: Mutex<Vec<Box<dyn Fn() + Send + Sync>>>,
|
||||
}
|
||||
|
||||
impl Plugin {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
on_start: Mutex::new(Vec::new()),
|
||||
on_tick: Mutex::new(Vec::new()),
|
||||
on_stop: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_start(&self, f: impl Fn() + Send + Sync + 'static) {
|
||||
self.on_start.lock().push(Box::new(f));
|
||||
}
|
||||
}
|
||||
|
||||
pub static PLUGIN: Plugin = Plugin::new();
|
3
plugin-api/src/raw_api/mod.rs
Normal file
3
plugin-api/src/raw_api/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
extern "C" {
|
||||
pub fn print(s: *const u8, len: usize);
|
||||
}
|
4
plugin-api/src/raw_hooks/mod.rs
Normal file
4
plugin-api/src/raw_hooks/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
use super::*;
|
||||
|
||||
// API
|
||||
pub extern "C" fn on_tick() { PLUGIN.on_tick.lock().iter().for_each(|f| f()); }
|
Loading…
Reference in New Issue
Block a user