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",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"spin",
|
"spin 0.5.2",
|
||||||
"untrusted",
|
"untrusted",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
@ -4495,6 +4495,12 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "652ac3743312871a5fb703f0337e68ffa3cdc28c863efad0b8dc858fa10c991b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin_sleep"
|
name = "spin_sleep"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -4922,9 +4928,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.5.6"
|
version = "0.5.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
|
checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@ -5346,11 +5352,21 @@ dependencies = [
|
|||||||
"spin_sleep",
|
"spin_sleep",
|
||||||
"structopt",
|
"structopt",
|
||||||
"sum_type",
|
"sum_type",
|
||||||
|
"tar",
|
||||||
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracy-client",
|
"tracy-client",
|
||||||
"vek 0.12.0",
|
"vek 0.12.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "veloren-plugin-api"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"spin 0.7.0",
|
||||||
|
"veloren-common",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "veloren-server"
|
name = "veloren-server"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
@ -5,6 +5,7 @@ members = [
|
|||||||
"common",
|
"common",
|
||||||
"common/sys",
|
"common/sys",
|
||||||
"client",
|
"client",
|
||||||
|
"plugin-api",
|
||||||
"server",
|
"server",
|
||||||
"server-cli",
|
"server-cli",
|
||||||
"voxygen",
|
"voxygen",
|
||||||
@ -75,6 +76,6 @@ inherits = 'release'
|
|||||||
debug = 1
|
debug = 1
|
||||||
|
|
||||||
[patch.crates-io]
|
[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" }
|
winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" }
|
||||||
vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics" }
|
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"
|
dot_vox = "4.0"
|
||||||
image = { version = "0.23.8", default-features = false, features = ["png"] }
|
image = { version = "0.23.8", default-features = false, features = ["png"] }
|
||||||
notify = "5.0.0-pre.3"
|
notify = "5.0.0-pre.3"
|
||||||
|
tar = "0.4.30"
|
||||||
|
|
||||||
# Auth
|
# Auth
|
||||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "b943c85e4a38f5ec60cd18c34c73097640162bfe" }
|
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 = { version = "1.0.110", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0.50"
|
serde_json = "1.0.50"
|
||||||
serde_repr = "0.1.6"
|
serde_repr = "0.1.6"
|
||||||
|
toml = "0.5.7"
|
||||||
|
|
||||||
#esv export
|
#esv export
|
||||||
csv = { version = "1.1.3", optional = true }
|
csv = { version = "1.1.3", optional = true }
|
||||||
|
@ -38,6 +38,7 @@ pub mod msg;
|
|||||||
pub mod npc;
|
pub mod npc;
|
||||||
pub mod outcome;
|
pub mod outcome;
|
||||||
pub mod path;
|
pub mod path;
|
||||||
|
pub mod plugin;
|
||||||
pub mod ray;
|
pub mod ray;
|
||||||
pub mod recipe;
|
pub mod recipe;
|
||||||
pub mod region;
|
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,
|
comp,
|
||||||
event::{EventBus, LocalEvent, ServerEvent},
|
event::{EventBus, LocalEvent, ServerEvent},
|
||||||
metrics::{PhysicsMetrics, SysMetrics},
|
metrics::{PhysicsMetrics, SysMetrics},
|
||||||
|
plugin::PluginMgr,
|
||||||
region::RegionMap,
|
region::RegionMap,
|
||||||
resources::{DeltaTime, Time, TimeOfDay},
|
resources::{DeltaTime, Time, TimeOfDay},
|
||||||
span,
|
span,
|
||||||
@ -18,6 +19,7 @@ use specs::{
|
|||||||
Component, DispatcherBuilder, Entity as EcsEntity, WorldExt,
|
Component, DispatcherBuilder, Entity as EcsEntity, WorldExt,
|
||||||
};
|
};
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
use tracing::info;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
/// How much faster should an in-game day be compared to a real day?
|
/// 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(SysMetrics::default());
|
||||||
ecs.insert(PhysicsMetrics::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
|
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