mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'ccgauche/moved-plugins-ecs' into 'master'
Improved ECS system for plugins to enable async execution of plugins in systems See merge request veloren/veloren!1857
This commit is contained in:
commit
b7079b454c
@ -1,14 +1,27 @@
|
|||||||
use std::sync::atomic::{AtomicPtr, AtomicU32, AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicPtr, AtomicU32, AtomicU64, Ordering};
|
||||||
|
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use specs::World;
|
use specs::{shred::Fetch, Entities, ReadStorage};
|
||||||
use wasmer::{Function, Memory, Value};
|
use wasmer::{Function, Memory, Value};
|
||||||
|
|
||||||
|
use common::{
|
||||||
|
comp::Health,
|
||||||
|
uid::{Uid, UidAllocator},
|
||||||
|
};
|
||||||
|
|
||||||
use super::errors::{MemoryAllocationError, PluginModuleError};
|
use super::errors::{MemoryAllocationError, PluginModuleError};
|
||||||
|
|
||||||
|
pub struct EcsWorld<'a> {
|
||||||
|
pub entities: Entities<'a>,
|
||||||
|
pub health: ReadStorage<'a, Health>,
|
||||||
|
pub uid: ReadStorage<'a, Uid>,
|
||||||
|
//pub player: ReadStorage<'a, Player>,
|
||||||
|
pub uid_allocator: Fetch<'a, UidAllocator>,
|
||||||
|
}
|
||||||
|
|
||||||
/// This structure wraps the ECS pointer to ensure safety
|
/// This structure wraps the ECS pointer to ensure safety
|
||||||
pub struct EcsAccessManager {
|
pub struct EcsAccessManager {
|
||||||
ecs_pointer: AtomicPtr<World>,
|
ecs_pointer: AtomicPtr<EcsWorld<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EcsAccessManager {
|
impl Default for EcsAccessManager {
|
||||||
@ -22,7 +35,7 @@ impl Default for EcsAccessManager {
|
|||||||
impl EcsAccessManager {
|
impl EcsAccessManager {
|
||||||
// This function take a World reference and a function to execute ensuring the
|
// This function take a World reference and a function to execute ensuring the
|
||||||
// pointer will never be corrupted during the execution of the function!
|
// pointer will never be corrupted during the execution of the function!
|
||||||
pub fn execute_with<T>(&self, world: &World, func: impl FnOnce() -> T) -> T {
|
pub fn execute_with<T>(&self, world: &EcsWorld, func: impl FnOnce() -> T) -> T {
|
||||||
let _guard = scopeguard::guard((), |_| {
|
let _guard = scopeguard::guard((), |_| {
|
||||||
// ensure the pointer is cleared in any case
|
// ensure the pointer is cleared in any case
|
||||||
self.ecs_pointer
|
self.ecs_pointer
|
||||||
@ -45,7 +58,7 @@ impl EcsAccessManager {
|
|||||||
/// reference somewhere else
|
/// reference somewhere else
|
||||||
/// - All that ensure that the reference doesn't exceed the execute_with
|
/// - All that ensure that the reference doesn't exceed the execute_with
|
||||||
/// function scope
|
/// function scope
|
||||||
pub unsafe fn get(&self) -> Option<&World> {
|
pub unsafe fn get(&self) -> Option<&EcsWorld> {
|
||||||
// ptr::as_ref will automatically check for null
|
// ptr::as_ref will automatically check for null
|
||||||
self.ecs_pointer.load(Ordering::Relaxed).as_ref()
|
self.ecs_pointer.load(Ordering::Relaxed).as_ref()
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ pub mod wasm_env;
|
|||||||
|
|
||||||
use common::assets::ASSETS_PATH;
|
use common::assets::ASSETS_PATH;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::World;
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fs,
|
fs,
|
||||||
@ -18,6 +17,7 @@ use plugin_api::Event;
|
|||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
errors::PluginError,
|
errors::PluginError,
|
||||||
|
memory_manager::EcsWorld,
|
||||||
module::{PluginModule, PreparedEventQuery},
|
module::{PluginModule, PreparedEventQuery},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ impl Plugin {
|
|||||||
|
|
||||||
pub fn execute_prepared<T>(
|
pub fn execute_prepared<T>(
|
||||||
&self,
|
&self,
|
||||||
ecs: &World,
|
ecs: &EcsWorld,
|
||||||
event: &PreparedEventQuery<T>,
|
event: &PreparedEventQuery<T>,
|
||||||
) -> Result<Vec<T::Response>, PluginError>
|
) -> Result<Vec<T::Response>, PluginError>
|
||||||
where
|
where
|
||||||
@ -121,7 +121,7 @@ impl PluginMgr {
|
|||||||
|
|
||||||
pub fn execute_prepared<T>(
|
pub fn execute_prepared<T>(
|
||||||
&self,
|
&self,
|
||||||
ecs: &World,
|
ecs: &EcsWorld,
|
||||||
event: &PreparedEventQuery<T>,
|
event: &PreparedEventQuery<T>,
|
||||||
) -> Result<Vec<T::Response>, PluginError>
|
) -> Result<Vec<T::Response>, PluginError>
|
||||||
where
|
where
|
||||||
@ -137,7 +137,11 @@ impl PluginMgr {
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_event<T>(&self, ecs: &World, event: &T) -> Result<Vec<T::Response>, PluginError>
|
pub fn execute_event<T>(
|
||||||
|
&self,
|
||||||
|
ecs: &EcsWorld,
|
||||||
|
event: &T,
|
||||||
|
) -> Result<Vec<T::Response>, PluginError>
|
||||||
where
|
where
|
||||||
T: Event,
|
T: Event,
|
||||||
{
|
{
|
||||||
|
@ -5,16 +5,12 @@ use std::{
|
|||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use common::{
|
use specs::saveload::MarkerAllocator;
|
||||||
comp::{Health, Player},
|
|
||||||
uid::UidAllocator,
|
|
||||||
};
|
|
||||||
use specs::{saveload::MarkerAllocator, World, WorldExt};
|
|
||||||
use wasmer::{imports, Cranelift, Function, Instance, Memory, Module, Store, Value, JIT};
|
use wasmer::{imports, Cranelift, Function, Instance, Memory, Module, Store, Value, JIT};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
errors::{PluginError, PluginModuleError},
|
errors::{PluginError, PluginModuleError},
|
||||||
memory_manager::{self, EcsAccessManager, MemoryManager},
|
memory_manager::{self, EcsAccessManager, EcsWorld, MemoryManager},
|
||||||
wasm_env::HostFunctionEnvironement,
|
wasm_env::HostFunctionEnvironement,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -110,7 +106,7 @@ impl PluginModule {
|
|||||||
/// return None if the event doesn't exists
|
/// return None if the event doesn't exists
|
||||||
pub fn try_execute<T>(
|
pub fn try_execute<T>(
|
||||||
&self,
|
&self,
|
||||||
ecs: &World,
|
ecs: &EcsWorld,
|
||||||
request: &PreparedEventQuery<T>,
|
request: &PreparedEventQuery<T>,
|
||||||
) -> Option<Result<T::Response, PluginModuleError>>
|
) -> Option<Result<T::Response, PluginModuleError>>
|
||||||
where
|
where
|
||||||
@ -236,31 +232,29 @@ fn retrieve_action(
|
|||||||
action: Retrieve,
|
action: Retrieve,
|
||||||
) -> Result<RetrieveResult, RetrieveError> {
|
) -> Result<RetrieveResult, RetrieveError> {
|
||||||
match action {
|
match action {
|
||||||
Retrieve::GetPlayerName(e) => {
|
Retrieve::GetPlayerName(_e) => {
|
||||||
// Safety: No reference is leaked out the function so it is safe.
|
// Safety: No reference is leaked out the function so it is safe.
|
||||||
let world = unsafe {
|
// let world = unsafe {
|
||||||
ecs.get().ok_or(RetrieveError::EcsAccessError(
|
// ecs.get().ok_or(RetrieveError::EcsAccessError(
|
||||||
EcsAccessError::EcsPointerNotAvailable,
|
// EcsAccessError::EcsPointerNotAvailable,
|
||||||
))?
|
// ))?
|
||||||
};
|
// };
|
||||||
let player = world
|
// let player = world.uid_allocator.retrieve_entity_internal(e.0).ok_or(
|
||||||
.read_resource::<UidAllocator>()
|
// RetrieveError::EcsAccessError(EcsAccessError::EcsEntityNotFound(e)),
|
||||||
.retrieve_entity_internal(e.0)
|
// )?;
|
||||||
.ok_or(RetrieveError::EcsAccessError(
|
|
||||||
EcsAccessError::EcsEntityNotFound(e),
|
|
||||||
))?;
|
|
||||||
Ok(RetrieveResult::GetPlayerName(
|
Ok(RetrieveResult::GetPlayerName(
|
||||||
world
|
"<TODO>".to_owned(), /* world
|
||||||
.read_component::<Player>()
|
* .player.get(player).ok_or_else(|| {
|
||||||
.get(player)
|
*
|
||||||
.ok_or_else(|| {
|
* RetrieveError::EcsAccessError(EcsAccessError::
|
||||||
RetrieveError::EcsAccessError(EcsAccessError::EcsComponentNotFound(
|
* EcsComponentNotFound(
|
||||||
e,
|
* e,
|
||||||
"Player".to_owned(),
|
* "Player".to_owned(),
|
||||||
))
|
* ))
|
||||||
})?
|
* })?
|
||||||
.alias
|
* .alias
|
||||||
.to_owned(),
|
* .to_owned() */
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
Retrieve::GetEntityHealth(e) => {
|
Retrieve::GetEntityHealth(e) => {
|
||||||
@ -270,22 +264,16 @@ fn retrieve_action(
|
|||||||
EcsAccessError::EcsPointerNotAvailable,
|
EcsAccessError::EcsPointerNotAvailable,
|
||||||
))?
|
))?
|
||||||
};
|
};
|
||||||
let player = world
|
let player = world.uid_allocator.retrieve_entity_internal(e.0).ok_or(
|
||||||
.read_resource::<UidAllocator>()
|
RetrieveError::EcsAccessError(EcsAccessError::EcsEntityNotFound(e)),
|
||||||
.retrieve_entity_internal(e.0)
|
)?;
|
||||||
.ok_or(RetrieveError::EcsAccessError(
|
|
||||||
EcsAccessError::EcsEntityNotFound(e),
|
|
||||||
))?;
|
|
||||||
Ok(RetrieveResult::GetEntityHealth(
|
Ok(RetrieveResult::GetEntityHealth(
|
||||||
*world
|
*world.health.get(player).ok_or_else(|| {
|
||||||
.read_component::<Health>()
|
RetrieveError::EcsAccessError(EcsAccessError::EcsComponentNotFound(
|
||||||
.get(player)
|
e,
|
||||||
.ok_or_else(|| {
|
"Health".to_owned(),
|
||||||
RetrieveError::EcsAccessError(EcsAccessError::EcsComponentNotFound(
|
))
|
||||||
e,
|
})?,
|
||||||
"Health".to_owned(),
|
|
||||||
))
|
|
||||||
})?,
|
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
|
use crate::plugin::memory_manager::EcsWorld;
|
||||||
|
#[cfg(feature = "plugins")]
|
||||||
use crate::plugin::PluginMgr;
|
use crate::plugin::PluginMgr;
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
@ -209,8 +211,17 @@ impl State {
|
|||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
ecs.insert(match PluginMgr::from_assets() {
|
ecs.insert(match PluginMgr::from_assets() {
|
||||||
Ok(plugin_mgr) => {
|
Ok(plugin_mgr) => {
|
||||||
|
let ecs_world = EcsWorld {
|
||||||
|
entities: ecs.entities(),
|
||||||
|
health: ecs.read_component(),
|
||||||
|
uid: ecs.read_component(),
|
||||||
|
uid_allocator: ecs.read_resource(),
|
||||||
|
//player: Either::First(ecs.read_component()),
|
||||||
|
};
|
||||||
if let Err(e) = plugin_mgr
|
if let Err(e) = plugin_mgr
|
||||||
.execute_event(&ecs, &plugin_api::event::PluginLoadEvent { game_mode })
|
.execute_event(&ecs_world, &plugin_api::event::PluginLoadEvent {
|
||||||
|
game_mode,
|
||||||
|
})
|
||||||
{
|
{
|
||||||
tracing::error!(?e, "Failed to run plugin init");
|
tracing::error!(?e, "Failed to run plugin init");
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
@ -221,7 +232,8 @@ impl State {
|
|||||||
plugin_mgr
|
plugin_mgr
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
|
tracing::error!(?e, "Failed to read plugins from assets");
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
"Error occurred when loading plugins. Running without plugins instead."
|
"Error occurred when loading plugins. Running without plugins instead."
|
||||||
);
|
);
|
||||||
|
@ -76,7 +76,7 @@ use common_net::{
|
|||||||
};
|
};
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
use common_sys::plugin::PluginMgr;
|
use common_sys::plugin::PluginMgr;
|
||||||
use common_sys::state::State;
|
use common_sys::{plugin::memory_manager::EcsWorld, state::State};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use metrics::{PhysicsMetrics, PlayerMetrics, StateTickMetrics, TickMetrics};
|
use metrics::{PhysicsMetrics, PlayerMetrics, StateTickMetrics, TickMetrics};
|
||||||
use network::{Network, Pid, ProtocolAddr};
|
use network::{Network, Pid, ProtocolAddr};
|
||||||
@ -1106,8 +1106,14 @@ impl Server {
|
|||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
{
|
{
|
||||||
let plugin_manager = self.state.ecs().read_resource::<PluginMgr>();
|
let plugin_manager = self.state.ecs().read_resource::<PluginMgr>();
|
||||||
|
let ecs_world = EcsWorld {
|
||||||
|
entities: self.state.ecs().entities(),
|
||||||
|
health: self.state.ecs().read_component(),
|
||||||
|
uid: self.state.ecs().read_component(),
|
||||||
|
uid_allocator: self.state.ecs().read_resource(),
|
||||||
|
};
|
||||||
let rs = plugin_manager.execute_event(
|
let rs = plugin_manager.execute_event(
|
||||||
self.state.ecs(),
|
&ecs_world,
|
||||||
&plugin_api::event::ChatCommandEvent {
|
&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(),
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use crate::settings::BanRecord;
|
use crate::settings::BanRecord;
|
||||||
use authc::{AuthClient, AuthClientError, AuthToken, Uuid};
|
use authc::{AuthClient, AuthClientError, AuthToken, Uuid};
|
||||||
use common_net::msg::RegisterError;
|
use common_net::msg::RegisterError;
|
||||||
|
use common_sys::plugin::memory_manager::EcsWorld;
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
use common_sys::plugin::PluginMgr;
|
use common_sys::plugin::PluginMgr;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use plugin_api::event::{PlayerJoinEvent, PlayerJoinResult};
|
use plugin_api::event::{PlayerJoinEvent, PlayerJoinResult};
|
||||||
use specs::World;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ impl LoginProvider {
|
|||||||
pub fn try_login(
|
pub fn try_login(
|
||||||
&mut self,
|
&mut self,
|
||||||
username_or_token: &str,
|
username_or_token: &str,
|
||||||
world: &World,
|
world: &EcsWorld,
|
||||||
#[cfg(feature = "plugins")] plugin_manager: &PluginMgr,
|
#[cfg(feature = "plugins")] plugin_manager: &PluginMgr,
|
||||||
admins: &HashSet<Uuid>,
|
admins: &HashSet<Uuid>,
|
||||||
whitelist: &HashSet<Uuid>,
|
whitelist: &HashSet<Uuid>,
|
||||||
@ -80,21 +80,23 @@ impl LoginProvider {
|
|||||||
return Err(RegisterError::NotOnWhitelist);
|
return Err(RegisterError::NotOnWhitelist);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
match plugin_manager.execute_event(&world, &PlayerJoinEvent {
|
{
|
||||||
player_name: username.clone(),
|
match plugin_manager.execute_event(&world, &PlayerJoinEvent {
|
||||||
player_id: *uuid.as_bytes(),
|
player_name: username.clone(),
|
||||||
}) {
|
player_id: *uuid.as_bytes(),
|
||||||
Ok(e) => {
|
}) {
|
||||||
for i in e.into_iter() {
|
Ok(e) => {
|
||||||
if let PlayerJoinResult::Kick(a) = i {
|
for i in e.into_iter() {
|
||||||
return Err(RegisterError::Kicked(a));
|
if let PlayerJoinResult::Kick(a) = i {
|
||||||
|
return Err(RegisterError::Kicked(a));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
Err(e) => {
|
||||||
Err(e) => {
|
error!("Error occured while executing `on_join`: {:?}",e);
|
||||||
error!("Error occured while executing `on_join`: {:?}",e);
|
},
|
||||||
},
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
// add the user to self.accounts
|
// add the user to self.accounts
|
||||||
self.login(uuid, username.clone())?;
|
self.login(uuid, username.clone())?;
|
||||||
|
@ -4,6 +4,7 @@ use common_net::msg::{
|
|||||||
ServerRegisterAnswer,
|
ServerRegisterAnswer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use common_sys::plugin::memory_manager::EcsWorld;
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
use common_sys::plugin::PluginMgr;
|
use common_sys::plugin::PluginMgr;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
@ -33,9 +34,15 @@ pub(crate) fn handle_register_msg(
|
|||||||
) -> Result<(), crate::error::Error> {
|
) -> Result<(), crate::error::Error> {
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
let plugin_mgr = world.read_resource::<PluginMgr>();
|
let plugin_mgr = world.read_resource::<PluginMgr>();
|
||||||
|
let ecs_world = EcsWorld {
|
||||||
|
entities: world.entities(),
|
||||||
|
health: world.read_component(),
|
||||||
|
uid: world.read_component(),
|
||||||
|
uid_allocator: world.read_resource(),
|
||||||
|
};
|
||||||
let (username, uuid) = match login_provider.try_login(
|
let (username, uuid) = match login_provider.try_login(
|
||||||
&msg.token_or_username,
|
&msg.token_or_username,
|
||||||
world,
|
&ecs_world,
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
&plugin_mgr,
|
&plugin_mgr,
|
||||||
&*editable_settings.admins,
|
&*editable_settings.admins,
|
||||||
|
Loading…
Reference in New Issue
Block a user