Resolved plugin dependency cycle, allowing more interesting plugin API

This commit is contained in:
Joshua Barretto 2020-12-13 12:27:48 +00:00
parent 8e937a50ca
commit 027842f832
20 changed files with 1620 additions and 68 deletions

12
Cargo.lock generated
View File

@ -5541,7 +5541,6 @@ version = "0.8.0"
dependencies = [ dependencies = [
"arraygen", "arraygen",
"authc", "authc",
"bincode",
"criterion", "criterion",
"crossbeam", "crossbeam",
"csv", "csv",
@ -5556,7 +5555,6 @@ dependencies = [
"num-derive", "num-derive",
"num-traits 0.2.12", "num-traits 0.2.12",
"ordered-float 2.0.0", "ordered-float 2.0.0",
"parking_lot 0.11.0",
"rand 0.7.3", "rand 0.7.3",
"rayon", "rayon",
"ron", "ron",
@ -5571,13 +5569,9 @@ 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",
"veloren-plugin-api",
"wasmer-runtime",
] ]
[[package]] [[package]]
@ -5585,6 +5579,7 @@ name = "veloren-plugin-api"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"serde", "serde",
"veloren-common",
] ]
[[package]] [[package]]
@ -5639,6 +5634,7 @@ dependencies = [
"uvth 3.1.1", "uvth 3.1.1",
"vek 0.12.0", "vek 0.12.0",
"veloren-common", "veloren-common",
"veloren-plugin-api",
"veloren-world", "veloren-world",
"veloren_common_sys", "veloren_common_sys",
"veloren_network", "veloren_network",
@ -5776,6 +5772,7 @@ dependencies = [
name = "veloren_common_sys" name = "veloren_common_sys"
version = "0.8.0" version = "0.8.0"
dependencies = [ dependencies = [
"bincode",
"hashbrown 0.7.2", "hashbrown 0.7.2",
"indexmap", "indexmap",
"rand 0.7.3", "rand 0.7.3",
@ -5783,11 +5780,14 @@ dependencies = [
"serde", "serde",
"slab", "slab",
"specs", "specs",
"tar",
"toml",
"tracing", "tracing",
"tracy-client", "tracy-client",
"vek 0.12.0", "vek 0.12.0",
"veloren-common", "veloren-common",
"veloren-plugin-api", "veloren-plugin-api",
"wasmer-runtime",
] ]
[[package]] [[package]]

Binary file not shown.

View File

@ -222,7 +222,7 @@ impl Client {
ability_map, ability_map,
} => { } => {
// Initialize `State` // Initialize `State`
let mut state = State::default(); let mut state = State::client();
// Client-only components // Client-only components
state state
.ecs_mut() .ecs_mut()

View File

@ -20,7 +20,6 @@ lazy_static = "1.4.0"
num-derive = "0.3" num-derive = "0.3"
num-traits = "0.2" num-traits = "0.2"
ordered-float = { version = "2.0.0", default-features = false } ordered-float = { version = "2.0.0", default-features = false }
parking_lot = "0.11.0"
rand = "0.7" rand = "0.7"
rayon = "1.3.0" rayon = "1.3.0"
roots = "0.0.6" roots = "0.0.6"
@ -34,7 +33,6 @@ 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" }
@ -54,7 +52,6 @@ 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 }
@ -63,11 +60,6 @@ structopt = { version = "0.3.13", optional = true }
# Tracy # Tracy
tracy-client = { version = "0.9.0", optional = true } tracy-client = { version = "0.9.0", optional = true }
# Plugins
wasmer-runtime = "0.17.1"
bincode = "1.3.1"
plugin-api = { package = "veloren-plugin-api", path = "../plugin/api"}
[dev-dependencies] [dev-dependencies]
#bench #bench
criterion = "0.3" criterion = "0.3"

View File

@ -3,9 +3,12 @@ use comp::{
item::{Item, Reagent}, item::{Item, Reagent},
Ori, Pos, Ori, Pos,
}; };
use parking_lot::Mutex;
use specs::Entity as EcsEntity; use specs::Entity as EcsEntity;
use std::{collections::VecDeque, ops::DerefMut}; use std::{
collections::VecDeque,
sync::Mutex,
ops::DerefMut,
};
use vek::*; use vek::*;
pub enum LocalEvent { pub enum LocalEvent {
@ -150,10 +153,10 @@ impl<E> EventBus<E> {
} }
} }
pub fn emit_now(&self, event: E) { self.queue.lock().push_back(event); } pub fn emit_now(&self, event: E) { self.queue.lock().unwrap().push_back(event); }
pub fn recv_all(&self) -> impl ExactSizeIterator<Item = E> { pub fn recv_all(&self) -> impl ExactSizeIterator<Item = E> {
std::mem::replace(self.queue.lock().deref_mut(), VecDeque::new()).into_iter() std::mem::replace(self.queue.lock().unwrap().deref_mut(), VecDeque::new()).into_iter()
} }
} }
@ -169,5 +172,5 @@ impl<'a, E> Emitter<'a, E> {
} }
impl<'a, E> Drop for Emitter<'a, E> { impl<'a, E> Drop for Emitter<'a, E> {
fn drop(&mut self) { self.bus.queue.lock().append(&mut self.events); } fn drop(&mut self) { self.bus.queue.lock().unwrap().append(&mut self.events); }
} }

View File

@ -38,7 +38,6 @@ 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;
@ -59,4 +58,3 @@ pub mod volumes;
pub use combat::{Damage, DamageSource, GroupTarget, Knockback}; pub use combat::{Damage, DamageSource, GroupTarget, Knockback};
pub use explosion::{Explosion, RadiusEffect}; pub use explosion::{Explosion, RadiusEffect};
pub use loadout_builder::LoadoutBuilder; pub use loadout_builder::LoadoutBuilder;
pub use plugin_api;

View File

@ -11,3 +11,15 @@ pub struct Time(pub f64);
/// A resource that stores the time since the previous tick. /// A resource that stores the time since the previous tick.
#[derive(Default)] #[derive(Default)]
pub struct DeltaTime(pub f32); pub struct DeltaTime(pub f32);
/// A resource that indicates what mode the local game is being played in.
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum GameMode {
/// The game is being played in server mode (i.e: the code is running server-side)
Server,
/// The game is being played in client mode (i.e: the code is running client-side)
Client,
/// The game is being played in singleplayer mode (i.e: both client and server at once)
// To be used later when we no longer start up an entirely new server for singleplayer
Singleplayer,
}

View File

@ -13,13 +13,6 @@ impl Into<u64> for Uid {
fn into(self) -> u64 { self.0 } fn into(self) -> u64 { self.0 }
} }
impl Into<plugin_api::Uid> for Uid {
fn into(self) -> plugin_api::Uid {
plugin_api::Uid(self.0)
}
}
impl From<u64> for Uid { impl From<u64> for Uid {
fn from(uid: u64) -> Self { Self(uid) } fn from(uid: u64) -> Self { Self(uid) }
} }

View File

@ -36,4 +36,8 @@ serde = { version = "1.0.110", features = ["derive"] }
tracy-client = { version = "0.9.0", optional = true } tracy-client = { version = "0.9.0", optional = true }
# Plugins # Plugins
toml = "0.5.7"
tar = "0.4.30"
wasmer-runtime = "0.17.1"
bincode = "1.3.1"
plugin-api = { package = "veloren-plugin-api", path = "../../plugin/api"} plugin-api = { package = "veloren-plugin-api", path = "../../plugin/api"}

View File

@ -9,6 +9,7 @@ pub mod controller;
pub mod melee; pub mod melee;
mod mount; mod mount;
pub mod phys; pub mod phys;
pub mod plugin;
mod projectile; mod projectile;
mod shockwave; mod shockwave;
pub mod state; pub mod state;

View File

@ -2,7 +2,7 @@
pub mod errors; pub mod errors;
pub mod module; pub mod module;
use crate::assets::ASSETS_PATH; use common::assets::ASSETS_PATH;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::{HashMap, HashSet}, fs, io::Read, path::{Path, PathBuf}}; use std::{collections::{HashMap, HashSet}, fs, io::Read, path::{Path, PathBuf}};
use tracing::{error, info}; use tracing::{error, info};

View File

@ -1,7 +1,11 @@
use std::{cell::Cell, collections::HashSet, marker::PhantomData, sync::Arc}; use std::{
cell::Cell,
collections::HashSet,
marker::PhantomData,
sync::{Arc, Mutex},
};
use error::RuntimeError; use error::RuntimeError;
use parking_lot::Mutex;
use wasmer_runtime::*; use wasmer_runtime::*;
use super::errors::{PluginError, PluginModuleError}; use super::errors::{PluginError, PluginModuleError};
@ -46,7 +50,7 @@ impl PluginModule {
return None; return None;
} }
let bytes = { let bytes = {
let instance = self.wasm_instance.lock(); let instance = self.wasm_instance.lock().unwrap();
let func = match instance.exports.get(event_name).map_err(|e| PluginModuleError::FunctionGet(e)) { let func = match instance.exports.get(event_name).map_err(|e| PluginModuleError::FunctionGet(e)) {
Ok(e) => e, Ok(e) => e,
Err(e) => return Some(Err(e)) Err(e) => return Some(Err(e))

View File

@ -2,7 +2,6 @@ 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,
@ -10,6 +9,7 @@ use common::{
terrain::{Block, TerrainChunk, TerrainGrid}, terrain::{Block, TerrainChunk, TerrainGrid},
time::DayPeriod, time::DayPeriod,
vol::{ReadVol, WriteVol}, vol::{ReadVol, WriteVol},
resources,
}; };
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use rayon::{ThreadPool, ThreadPoolBuilder}; use rayon::{ThreadPool, ThreadPoolBuilder};
@ -21,6 +21,7 @@ use specs::{
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
use tracing::info; use tracing::info;
use vek::*; use vek::*;
use crate::plugin::PluginMgr;
/// 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?
// TODO: Don't hard-code this. // TODO: Don't hard-code this.
@ -70,6 +71,13 @@ impl TerrainChanges {
} }
} }
#[derive(Copy, Clone)]
pub enum ExecMode {
Server,
Client,
Singleplayer,
}
/// A type used to represent game state stored on both the client and the /// A type used to represent game state stored on both the client and the
/// server. This includes things like entity components, terrain data, and /// server. This includes things like entity components, terrain data, and
/// global states like weather, time of day, etc. /// global states like weather, time of day, etc.
@ -79,21 +87,23 @@ pub struct State {
thread_pool: Arc<ThreadPool>, thread_pool: Arc<ThreadPool>,
} }
impl Default for State { impl State {
/// Create a new `State`. /// Create a new `State` in client mode.
fn default() -> Self { pub fn client() -> Self { Self::new(resources::GameMode::Client) }
/// Create a new `State` in server mode.
pub fn server() -> Self { Self::new(resources::GameMode::Server) }
pub fn new(game_mode: resources::GameMode) -> Self {
Self { Self {
ecs: Self::setup_ecs_world(), ecs: Self::setup_ecs_world(game_mode),
thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()), thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()),
} }
} }
}
impl State {
/// Creates ecs world and registers all the common components and resources /// Creates ecs world and registers all the common components and resources
// TODO: Split up registering into server and client (e.g. move // TODO: Split up registering into server and client (e.g. move
// EventBus<ServerEvent> to the server) // EventBus<ServerEvent> to the server)
fn setup_ecs_world() -> specs::World { fn setup_ecs_world(game_mode: resources::GameMode) -> specs::World {
let mut ecs = specs::World::new(); let mut ecs = specs::World::new();
// Uids for sync // Uids for sync
ecs.register_sync_marker(); ecs.register_sync_marker();
@ -171,6 +181,7 @@ impl State {
ecs.insert(BlockChange::default()); ecs.insert(BlockChange::default());
ecs.insert(TerrainChanges::default()); ecs.insert(TerrainChanges::default());
ecs.insert(EventBus::<LocalEvent>::default()); ecs.insert(EventBus::<LocalEvent>::default());
ecs.insert(game_mode);
// TODO: only register on the server // TODO: only register on the server
ecs.insert(EventBus::<ServerEvent>::default()); ecs.insert(EventBus::<ServerEvent>::default());
ecs.insert(comp::group::GroupManager::default()); ecs.insert(comp::group::GroupManager::default());
@ -181,7 +192,9 @@ impl State {
// Load plugins from asset directory // Load plugins from asset directory
ecs.insert(match PluginMgr::from_assets() { ecs.insert(match PluginMgr::from_assets() {
Ok(plugin_mgr) => { Ok(plugin_mgr) => {
if let Err(e) = plugin_mgr.execute_event("on_load", &plugin_api::event::PluginLoadEvent {}) { if let Err(e) = plugin_mgr.execute_event("on_load", &plugin_api::event::PluginLoadEvent {
game_mode,
}) {
tracing::error!(?e, "Failed to run plugin init"); tracing::error!(?e, "Failed to run plugin init");
info!("Error occurred when loading plugins. Running without plugins instead."); info!("Error occurred when loading plugins. Running without plugins instead.");
PluginMgr::default() PluginMgr::default()

View File

@ -4,7 +4,6 @@ version = "0.1.0"
authors = ["ccgauche <gaucheron.laurent@gmail.com>"] authors = ["ccgauche <gaucheron.laurent@gmail.com>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
serde = {version = "1.0.118", features = ["derive"]} common = { package = "veloren-common", path = "../../common", features = ["no-assets"] }
serde = { version = "1.0.118", features = ["derive"] }

View File

@ -1,4 +1,5 @@
use serde::{Serialize, de::DeserializeOwned, Deserialize}; use serde::{Serialize, de::DeserializeOwned, Deserialize};
use common::{sync, resources};
#[derive(Deserialize,Serialize,Debug)] #[derive(Deserialize,Serialize,Debug)]
pub enum Action { pub enum Action {
@ -12,16 +13,7 @@ pub trait Event: Serialize + DeserializeOwned + Send + Sync{
type Response: Serialize + DeserializeOwned + Send + Sync; type Response: Serialize + DeserializeOwned + Send + Sync;
} }
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] pub use resources::GameMode;
pub enum PluginMode {
Server,
Client,
Singleplayer, // To be used later when we no longer start up an entirely new server for singleplayer
}
// TODO: Unify this with common/src/comp/uid.rs:Uid
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Uid(pub u64);
pub mod event { pub mod event {
use super::*; use super::*;
@ -40,7 +32,7 @@ pub mod event {
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct Player { pub struct Player {
pub id: Uid, pub id: sync::Uid,
} }
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
@ -67,7 +59,7 @@ pub mod event {
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct PluginLoadEvent { pub struct PluginLoadEvent {
pub mode: PluginMode, pub game_mode: GameMode,
} }
impl Event for PluginLoadEvent { impl Event for PluginLoadEvent {

1521
plugin/hello/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,15 @@
extern crate plugin_rt; extern crate plugin_rt;
use plugin_rt::*; use plugin_rt::*;
use plugin_rt::api::{Action, event::*}; use plugin_rt::api::{Action, GameMode, event::*};
#[event_handler] #[event_handler]
pub fn on_load(load: PluginLoadEvent) -> () { pub fn on_load(load: PluginLoadEvent) {
emit_action(Action::Print("This is a test".to_owned())); match load.game_mode {
println!("Hello world"); GameMode::Server => emit_action(Action::Print("Hello, server!".to_owned())),
GameMode::Client => emit_action(Action::Print("Hello, client!".to_owned())),
GameMode::Singleplayer => emit_action(Action::Print("Hello, singleplayer!".to_owned())),
}
} }
#[event_handler] #[event_handler]

View File

@ -45,3 +45,6 @@ diesel = { version = "1.4.3", features = ["sqlite"] }
diesel_migrations = "1.4.0" diesel_migrations = "1.4.0"
dotenv = "0.15.0" dotenv = "0.15.0"
slab = "0.4" slab = "0.4"
# Plugins
plugin-api = { package = "veloren-plugin-api", path = "../plugin/api"}

View File

@ -46,10 +46,24 @@ use crate::{
state_ext::StateExt, state_ext::StateExt,
sys::sentinel::{DeletedEntities, TrackedComps}, sys::sentinel::{DeletedEntities, TrackedComps},
}; };
use common::{assets::Asset, cmd::ChatCommand, comp::{self, ChatType}, event::{EventBus, ServerEvent}, msg::{ use common::{
ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerInit, ServerMsg, WorldMapMsg, assets::Asset,
}, outcome::Outcome, plugin::PluginMgr, recipe::default_recipe_book, resources::TimeOfDay, rtsim::RtSimEntity, sync::{Uid, WorldSyncExt}, terrain::TerrainChunkSize, vol::{ReadVol, RectVolSize}}; cmd::ChatCommand,
use common_sys::state::State; comp::{self, ChatType},
event::{EventBus, ServerEvent},
msg::{ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerInit, ServerMsg, WorldMapMsg},
outcome::Outcome,
recipe::default_recipe_book,
resources::TimeOfDay,
rtsim::RtSimEntity,
sync::{Uid, WorldSyncExt},
terrain::TerrainChunkSize,
vol::{ReadVol, RectVolSize},
};
use common_sys::{
state::State,
plugin::PluginMgr,
};
use futures_executor::block_on; use futures_executor::block_on;
use metrics::{PhysicsMetrics, ServerMetrics, StateTickMetrics, TickMetrics}; use metrics::{PhysicsMetrics, ServerMetrics, StateTickMetrics, TickMetrics};
use network::{Network, Pid, ProtocolAddr}; use network::{Network, Pid, ProtocolAddr};
@ -131,7 +145,7 @@ impl Server {
metrics::NetworkRequestMetrics::new().unwrap(); metrics::NetworkRequestMetrics::new().unwrap();
let (player_metrics, registry_player) = metrics::PlayerMetrics::new().unwrap(); let (player_metrics, registry_player) = metrics::PlayerMetrics::new().unwrap();
let mut state = State::default(); let mut state = State::server();
state.ecs_mut().insert(settings.clone()); state.ecs_mut().insert(settings.clone());
state.ecs_mut().insert(editable_settings); state.ecs_mut().insert(editable_settings);
state.ecs_mut().insert(DataDir { state.ecs_mut().insert(DataDir {
@ -996,10 +1010,10 @@ impl Server {
command.execute(self, entity, args); command.execute(self, entity, args);
} else { } else {
let plugin_manager = self.state.ecs().read_resource::<PluginMgr>(); let plugin_manager = self.state.ecs().read_resource::<PluginMgr>();
let rs = plugin_manager.execute_event(&format!("on_command_{}",&kwd), &common::plugin_api::event::ChatCommandEvent { let rs = plugin_manager.execute_event(&format!("on_command_{}",&kwd), &plugin_api::event::ChatCommandEvent {
command: kwd.clone(), command: kwd.clone(),
command_args: args.split(" ").map(|x| x.to_owned()).collect(), command_args: args.split(" ").map(|x| x.to_owned()).collect(),
player: common::plugin_api::event::Player { player: plugin_api::event::Player {
id: (*(self.state.ecs().read_storage::<Uid>().get(entity).expect("Can't get player UUID [This should never appen]"))).into() id: (*(self.state.ecs().read_storage::<Uid>().get(entity).expect("Can't get player UUID [This should never appen]"))).into()
}, },
}); });