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