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

Binary file not shown.

View File

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

View File

@ -20,7 +20,6 @@ lazy_static = "1.4.0"
num-derive = "0.3"
num-traits = "0.2"
ordered-float = { version = "2.0.0", default-features = false }
parking_lot = "0.11.0"
rand = "0.7"
rayon = "1.3.0"
roots = "0.0.6"
@ -34,7 +33,6 @@ 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" }
@ -54,7 +52,6 @@ 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 }
@ -63,11 +60,6 @@ structopt = { version = "0.3.13", optional = true }
# Tracy
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]
#bench
criterion = "0.3"

View File

@ -3,9 +3,12 @@ use comp::{
item::{Item, Reagent},
Ori, Pos,
};
use parking_lot::Mutex;
use specs::Entity as EcsEntity;
use std::{collections::VecDeque, ops::DerefMut};
use std::{
collections::VecDeque,
sync::Mutex,
ops::DerefMut,
};
use vek::*;
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> {
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> {
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 outcome;
pub mod path;
pub mod plugin;
pub mod ray;
pub mod recipe;
pub mod region;
@ -59,4 +58,3 @@ pub mod volumes;
pub use combat::{Damage, DamageSource, GroupTarget, Knockback};
pub use explosion::{Explosion, RadiusEffect};
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.
#[derive(Default)]
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 }
}
impl Into<plugin_api::Uid> for Uid {
fn into(self) -> plugin_api::Uid {
plugin_api::Uid(self.0)
}
}
impl From<u64> for 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 }
# 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"}

View File

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

View File

@ -2,7 +2,7 @@
pub mod errors;
pub mod module;
use crate::assets::ASSETS_PATH;
use common::assets::ASSETS_PATH;
use serde::{Deserialize, Serialize};
use std::{collections::{HashMap, HashSet}, fs, io::Read, path::{Path, PathBuf}};
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 parking_lot::Mutex;
use wasmer_runtime::*;
use super::errors::{PluginError, PluginModuleError};
@ -46,7 +50,7 @@ impl PluginModule {
return None;
}
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)) {
Ok(e) => e,
Err(e) => return Some(Err(e))

View File

@ -2,7 +2,6 @@ use common::{
comp,
event::{EventBus, LocalEvent, ServerEvent},
metrics::{PhysicsMetrics, SysMetrics},
plugin::PluginMgr,
region::RegionMap,
resources::{DeltaTime, Time, TimeOfDay},
span,
@ -10,6 +9,7 @@ use common::{
terrain::{Block, TerrainChunk, TerrainGrid},
time::DayPeriod,
vol::{ReadVol, WriteVol},
resources,
};
use hashbrown::{HashMap, HashSet};
use rayon::{ThreadPool, ThreadPoolBuilder};
@ -21,6 +21,7 @@ use specs::{
use std::{sync::Arc, time::Duration};
use tracing::info;
use vek::*;
use crate::plugin::PluginMgr;
/// How much faster should an in-game day be compared to a real day?
// 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
/// server. This includes things like entity components, terrain data, and
/// global states like weather, time of day, etc.
@ -79,21 +87,23 @@ pub struct State {
thread_pool: Arc<ThreadPool>,
}
impl Default for State {
/// Create a new `State`.
fn default() -> Self {
impl State {
/// Create a new `State` in client mode.
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 {
ecs: Self::setup_ecs_world(),
ecs: Self::setup_ecs_world(game_mode),
thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()),
}
}
}
impl State {
/// Creates ecs world and registers all the common components and resources
// TODO: Split up registering into server and client (e.g. move
// 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();
// Uids for sync
ecs.register_sync_marker();
@ -171,6 +181,7 @@ impl State {
ecs.insert(BlockChange::default());
ecs.insert(TerrainChanges::default());
ecs.insert(EventBus::<LocalEvent>::default());
ecs.insert(game_mode);
// TODO: only register on the server
ecs.insert(EventBus::<ServerEvent>::default());
ecs.insert(comp::group::GroupManager::default());
@ -181,7 +192,9 @@ impl State {
// Load plugins from asset directory
ecs.insert(match PluginMgr::from_assets() {
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");
info!("Error occurred when loading plugins. Running without plugins instead.");
PluginMgr::default()

View File

@ -4,7 +4,6 @@ version = "0.1.0"
authors = ["ccgauche <gaucheron.laurent@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
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 common::{sync, resources};
#[derive(Deserialize,Serialize,Debug)]
pub enum Action {
@ -12,16 +13,7 @@ pub trait Event: Serialize + DeserializeOwned + Send + Sync{
type Response: Serialize + DeserializeOwned + Send + Sync;
}
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
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 use resources::GameMode;
pub mod event {
use super::*;
@ -40,7 +32,7 @@ pub mod event {
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct Player {
pub id: Uid,
pub id: sync::Uid,
}
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
@ -67,7 +59,7 @@ pub mod event {
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct PluginLoadEvent {
pub mode: PluginMode,
pub game_mode: GameMode,
}
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;
use plugin_rt::*;
use plugin_rt::api::{Action, event::*};
use plugin_rt::api::{Action, GameMode, event::*};
#[event_handler]
pub fn on_load(load: PluginLoadEvent) -> () {
emit_action(Action::Print("This is a test".to_owned()));
println!("Hello world");
pub fn on_load(load: PluginLoadEvent) {
match load.game_mode {
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]

View File

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

View File

@ -46,10 +46,24 @@ use crate::{
state_ext::StateExt,
sys::sentinel::{DeletedEntities, TrackedComps},
};
use common::{assets::Asset, cmd::ChatCommand, comp::{self, ChatType}, event::{EventBus, ServerEvent}, msg::{
ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerInit, ServerMsg, WorldMapMsg,
}, outcome::Outcome, plugin::PluginMgr, recipe::default_recipe_book, resources::TimeOfDay, rtsim::RtSimEntity, sync::{Uid, WorldSyncExt}, terrain::TerrainChunkSize, vol::{ReadVol, RectVolSize}};
use common_sys::state::State;
use common::{
assets::Asset,
cmd::ChatCommand,
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 metrics::{PhysicsMetrics, ServerMetrics, StateTickMetrics, TickMetrics};
use network::{Network, Pid, ProtocolAddr};
@ -131,7 +145,7 @@ impl Server {
metrics::NetworkRequestMetrics::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(editable_settings);
state.ecs_mut().insert(DataDir {
@ -996,10 +1010,10 @@ impl Server {
command.execute(self, entity, args);
} else {
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_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()
},
});