From 927b2868ec93916737a4a807b29815cc8855c6d7 Mon Sep 17 00:00:00 2001 From: ccgauche Date: Mon, 1 Mar 2021 19:00:44 +0100 Subject: [PATCH] Lots of improvement in pointer management (switched from i32 to u64) + New event implemented --- client/src/error.rs | 1 + client/src/lib.rs | 1 + common/net/src/msg/server.rs | 1 + common/src/comp/mod.rs | 2 +- common/sys/src/plugin/memory_manager.rs | 58 +++-- common/sys/src/plugin/module.rs | 35 ++-- common/sys/src/plugin/wasm_env.rs | 19 +- plugin/api/src/lib.rs | 5 +- plugin/derive/src/lib.rs | 4 +- plugin/rt/examples/hello.rs | 14 +- plugin/rt/src/lib.rs | 49 +++-- server/src/lib.rs | 2 +- server/src/login_provider.rs | 21 ++ server/src/sys/msg/register.rs | 268 +++++++++++------------- voxygen/src/menu/main/mod.rs | 1 + 15 files changed, 281 insertions(+), 200 deletions(-) diff --git a/client/src/error.rs b/client/src/error.rs index 2a6ac310ef..ef2a94f34c 100644 --- a/client/src/error.rs +++ b/client/src/error.rs @@ -4,6 +4,7 @@ use network::{ParticipantError, StreamError}; #[derive(Debug)] pub enum Error { + KickedByPlugin(String), NetworkErr(NetworkError), ParticipantErr(ParticipantError), StreamErr(StreamError), diff --git a/client/src/lib.rs b/client/src/lib.rs index c30c5db091..866904ec78 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -500,6 +500,7 @@ impl Client { Err(RegisterError::AuthError(err)) => Err(Error::AuthErr(err)), Err(RegisterError::InvalidCharacter) => Err(Error::InvalidCharacter), Err(RegisterError::NotOnWhitelist) => Err(Error::NotOnWhitelist), + Err(RegisterError::KickedByPlugin(err)) => Err(Error::KickedByPlugin(err)), Err(RegisterError::Banned(reason)) => Err(Error::Banned(reason)), Ok(()) => { self.registered = true; diff --git a/common/net/src/msg/server.rs b/common/net/src/msg/server.rs index 2f55c0d21f..146bde5773 100644 --- a/common/net/src/msg/server.rs +++ b/common/net/src/msg/server.rs @@ -191,6 +191,7 @@ pub enum RegisterError { AlreadyLoggedIn, AuthError(String), Banned(String), + KickedByPlugin(String), InvalidCharacter, NotOnWhitelist, //TODO: InvalidAlias, diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 8e342cde4e..f54f44abed 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -8,7 +8,7 @@ pub mod buff; #[cfg(not(target_arch = "wasm32"))] mod character_state; #[cfg(not(target_arch = "wasm32"))] pub mod chat; -pub mod combo; +#[cfg(not(target_arch = "wasm32"))] pub mod combo; #[cfg(not(target_arch = "wasm32"))] mod controller; #[cfg(not(target_arch = "wasm32"))] mod energy; diff --git a/common/sys/src/plugin/memory_manager.rs b/common/sys/src/plugin/memory_manager.rs index 771e78b9e4..5d317fe0fa 100644 --- a/common/sys/src/plugin/memory_manager.rs +++ b/common/sys/src/plugin/memory_manager.rs @@ -1,4 +1,4 @@ -use std::sync::atomic::{AtomicI32, AtomicPtr, AtomicU32, Ordering}; +use std::sync::atomic::{AtomicPtr, AtomicU32, AtomicU64, Ordering}; use serde::{de::DeserializeOwned, Serialize}; use specs::World; @@ -52,14 +52,14 @@ impl EcsAccessManager { } pub struct MemoryManager { - pub pointer: AtomicI32, + pub pointer: AtomicU64, pub length: AtomicU32, } impl Default for MemoryManager { fn default() -> Self { Self { - pointer: AtomicI32::new(0), + pointer: AtomicU64::new(0), length: AtomicU32::new(0), } } @@ -74,16 +74,18 @@ impl MemoryManager { &self, object_length: u32, allocator: &Function, - ) -> Result { + ) -> Result { if self.length.load(Ordering::SeqCst) >= object_length { return Ok(self.pointer.load(Ordering::SeqCst)); } let pointer = allocator .call(&[Value::I32(object_length as i32)]) .map_err(MemoryAllocationError::CantAllocate)?; - let pointer = pointer[0] - .i32() - .ok_or(MemoryAllocationError::InvalidReturnType)?; + let pointer = super::module::from_i64( + pointer[0] + .i64() + .ok_or(MemoryAllocationError::InvalidReturnType)?, + ); self.length.store(object_length, Ordering::SeqCst); self.pointer.store(pointer, Ordering::SeqCst); Ok(pointer) @@ -96,7 +98,7 @@ impl MemoryManager { memory: &Memory, allocator: &Function, object: &T, - ) -> Result<(i32, u32), PluginModuleError> { + ) -> Result<(u64, u64), PluginModuleError> { self.write_bytes( memory, allocator, @@ -104,6 +106,19 @@ impl MemoryManager { ) } + pub fn write_data_as_pointer( + &self, + memory: &Memory, + allocator: &Function, + object: &T, + ) -> Result { + self.write_bytes_as_pointer( + memory, + allocator, + &bincode::serialize(object).map_err(PluginModuleError::Encoding)?, + ) + } + /// This function writes an raw bytes to WASM memory returning a pointer and /// a length. Will realloc the buffer is not wide enough pub fn write_bytes( @@ -111,7 +126,7 @@ impl MemoryManager { memory: &Memory, allocator: &Function, array: &[u8], - ) -> Result<(i32, u32), PluginModuleError> { + ) -> Result<(u64, u64), PluginModuleError> { let len = array.len(); let mem_position = self .get_pointer(len as u32, allocator) @@ -120,7 +135,24 @@ impl MemoryManager { .iter() .zip(array.iter()) .for_each(|(cell, byte)| cell.set(*byte)); - Ok((mem_position as i32, len as u32)) + Ok((mem_position as u64, len as u64)) + } + + pub fn write_bytes_as_pointer( + &self, + memory: &Memory, + allocator: &Function, + array: &[u8], + ) -> Result { + let len = array.len(); + let mem_position = self + .get_pointer(len as u32 + 8, allocator) + .map_err(PluginModuleError::MemoryAllocation)? as usize; + memory.view()[mem_position..mem_position + len + 8] + .iter() + .zip((len as u64).to_le_bytes().iter().chain(array.iter())) + .for_each(|(cell, byte)| cell.set(*byte)); + Ok(mem_position as u64) } } @@ -128,14 +160,14 @@ impl MemoryManager { /// converts it to an object using bincode pub fn read_data( memory: &Memory, - position: i32, - length: u32, + position: u64, + length: u64, ) -> Result { bincode::deserialize(&read_bytes(memory, position, length)) } /// This function read raw bytes from memory at a position with the array length -pub fn read_bytes(memory: &Memory, position: i32, length: u32) -> Vec { +pub fn read_bytes(memory: &Memory, position: u64, length: u64) -> Vec { memory.view()[(position as usize)..(position as usize) + length as usize] .iter() .map(|x| x.get()) diff --git a/common/sys/src/plugin/module.rs b/common/sys/src/plugin/module.rs index c8b16d613c..248e068237 100644 --- a/common/sys/src/plugin/module.rs +++ b/common/sys/src/plugin/module.rs @@ -43,8 +43,8 @@ impl PluginModule { let module = Module::new(&store, &wasm_data).expect("Can't compile"); // This is the function imported into the wasm environement - fn raw_emit_actions(env: &HostFunctionEnvironement, ptr: u32, len: u32) { - handle_actions(match env.read_data(ptr as i32, len) { + fn raw_emit_actions(env: &HostFunctionEnvironement, ptr: i64, len: i64) { + handle_actions(match env.read_data(from_i64(ptr), from_i64(len)) { Ok(e) => e, Err(e) => { tracing::error!(?e, "Can't decode action"); @@ -53,16 +53,15 @@ impl PluginModule { }); } - fn raw_retrieve_action(env: &HostFunctionEnvironement, ptr: u32, len: u32) -> i64 { - let out = match env.read_data(ptr as _, len) { + fn raw_retrieve_action(env: &HostFunctionEnvironement, ptr: i64, len: i64) -> i64 { + let out = match env.read_data(from_i64(ptr), from_i64(len)) { Ok(data) => retrieve_action(&env.ecs, data), Err(e) => Err(RetrieveError::BincodeError(e.to_string())), }; // If an error happen set the i64 to 0 so the WASM side can tell an error // occured - let (ptr, len) = env.write_data(&out).unwrap(); - to_i64(ptr, len as _) + to_i64(env.write_data_as_pointer(&out).unwrap()) } fn dbg(a: i32) { @@ -155,20 +154,24 @@ impl PreparedEventQuery { } } -fn from_i64(i: i64) -> (i32, i32) { +pub fn from_u128(i: u128) -> (u64, u64) { let i = i.to_le_bytes(); ( - i32::from_le_bytes(i[0..4].try_into().unwrap()), - i32::from_le_bytes(i[4..8].try_into().unwrap()), + u64::from_le_bytes(i[0..8].try_into().unwrap()), + u64::from_le_bytes(i[8..16].try_into().unwrap()), ) } -pub fn to_i64(a: i32, b: i32) -> i64 { +pub fn to_u128(a: u64, b: u64) -> u128 { let a = a.to_le_bytes(); let b = b.to_le_bytes(); - i64::from_le_bytes([a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3]]) + u128::from_le_bytes([a, b].concat().try_into().unwrap()) } +pub fn to_i64(i: u64) -> i64 { i64::from_le_bytes(i.to_le_bytes()) } + +pub fn from_i64(i: i64) -> u64 { u64::from_le_bytes(i.to_le_bytes()) } + // This function is not public because this function should not be used without // an interface to limit unsafe behaviours #[allow(clippy::needless_range_loop)] @@ -196,24 +199,26 @@ fn execute_raw( // We call the function with the pointer and the length let function_result = func - .call(&[Value::I32(mem_position as i32), Value::I32(len as i32)]) + .call(&[Value::I64(to_i64(mem_position)), Value::I64(to_i64(len))]) .map_err(PluginModuleError::RunFunction)?; // Waiting for `multi-value` to be added to LLVM. So we encode the two i32 as an // i64 - let (pointer, length) = from_i64( + let u128_pointer = from_i64( function_result[0] .i64() .ok_or_else(PluginModuleError::InvalidArgumentType)?, ); + let bytes = memory_manager::read_bytes(&module.memory, u128_pointer, 16); + // We read the return object and deserialize it Ok(memory_manager::read_bytes( &module.memory, - pointer, - length as u32, + u64::from_le_bytes(bytes[0..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), )) } diff --git a/common/sys/src/plugin/wasm_env.rs b/common/sys/src/plugin/wasm_env.rs index e383f7f3d5..bcd5ec3d55 100644 --- a/common/sys/src/plugin/wasm_env.rs +++ b/common/sys/src/plugin/wasm_env.rs @@ -37,7 +37,7 @@ impl HostFunctionEnvironement { /// This function is a safe interface to WASM memory that writes data to the /// memory returning a pointer and length - pub fn write_data(&self, object: &T) -> Result<(i32, u32), PluginModuleError> { + pub fn write_data(&self, object: &T) -> Result<(u64, u64), PluginModuleError> { self.memory_manager.write_data( self.memory.get_ref().unwrap(), self.allocator.get_ref().unwrap(), @@ -45,12 +45,25 @@ impl HostFunctionEnvironement { ) } + /// This function is a safe interface to WASM memory that writes data to the + /// memory returning a pointer and length + pub fn write_data_as_pointer( + &self, + object: &T, + ) -> Result { + self.memory_manager.write_data_as_pointer( + self.memory.get_ref().unwrap(), + self.allocator.get_ref().unwrap(), + object, + ) + } + /// This function is a safe interface to WASM memory that reads memory from /// pointer and length returning an object pub fn read_data( &self, - position: i32, - length: u32, + position: u64, + length: u64, ) -> Result { memory_manager::read_data(self.memory.get_ref().unwrap(), position, length) } diff --git a/plugin/api/src/lib.rs b/plugin/api/src/lib.rs index 043ac0ea0f..2d159b3c25 100644 --- a/plugin/api/src/lib.rs +++ b/plugin/api/src/lib.rs @@ -56,7 +56,7 @@ pub mod event { #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] pub struct PlayerJoinEvent { pub player_name: String, - pub player_id: Uid, + pub player_id: [u8; 16], } impl Event for PlayerJoinEvent { @@ -64,8 +64,9 @@ pub mod event { } #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] + #[repr(u8)] pub enum PlayerJoinResult { - CloseConnection, + CloseConnection(String), None, } diff --git a/plugin/derive/src/lib.rs b/plugin/derive/src/lib.rs index 69fd927fa5..d275848d87 100644 --- a/plugin/derive/src/lib.rs +++ b/plugin/derive/src/lib.rs @@ -16,8 +16,8 @@ pub fn event_handler(_args: TokenStream, item: TokenStream) -> TokenStream { let out: proc_macro2::TokenStream = quote! { #[allow(clippy::unnecessary_wraps)] #[no_mangle] - pub fn #fn_name(intern__ptr: i32, intern__len: i32) -> i64 { - let input = ::veloren_plugin_rt::read_input(intern__ptr,intern__len as u32).unwrap(); + pub fn #fn_name(intern__ptr: i64, intern__len: i64) -> i64 { + let input = ::veloren_plugin_rt::read_input(intern__ptr as _,intern__len as _).unwrap(); #[inline] fn inner(#fn_args) #fn_return { #fn_body diff --git a/plugin/rt/examples/hello.rs b/plugin/rt/examples/hello.rs index bd4ad051db..752a6f32b8 100644 --- a/plugin/rt/examples/hello.rs +++ b/plugin/rt/examples/hello.rs @@ -1,3 +1,5 @@ +use std::sync::atomic::{AtomicBool, Ordering}; + use veloren_plugin_rt::{ api::{event::*, Action, GameMode}, *, @@ -29,14 +31,12 @@ pub fn on_command_testplugin(command: ChatCommandEvent) -> Result, S )]) } +static COUNTER: AtomicBool = AtomicBool::new(false); + #[event_handler] -pub fn on_player_join(input: PlayerJoinEvent) -> PlayerJoinResult { - emit_action(Action::PlayerSendMessage( - input.player_id, - format!("Welcome {} on our server", input.player_name), - )); - if input.player_name == "Cheater123" { - PlayerJoinResult::CloseConnection +pub fn on_join(input: PlayerJoinEvent) -> PlayerJoinResult { + if COUNTER.swap(!COUNTER.load(Ordering::SeqCst), Ordering::SeqCst) { + PlayerJoinResult::CloseConnection(format!("You are a cheater {:?}", input)) } else { PlayerJoinResult::None } diff --git a/plugin/rt/src/lib.rs b/plugin/rt/src/lib.rs index 954f412e52..78e1990395 100644 --- a/plugin/rt/src/lib.rs +++ b/plugin/rt/src/lib.rs @@ -18,8 +18,8 @@ use serde::{de::DeserializeOwned, Serialize}; #[cfg(target_arch = "wasm32")] extern "C" { - fn raw_emit_actions(ptr: *const u8, len: usize); - fn raw_retrieve_action(ptr: *const u8, len: usize) -> i64; + fn raw_emit_actions(ptr: i64, len: i64); + fn raw_retrieve_action(ptr: i64, len: i64) -> i64; pub fn dbg(i: i32); } @@ -28,8 +28,11 @@ pub fn retrieve_action(_actions: &api::Retrieve) -> Result< { let ret = bincode::serialize(&_actions).expect("Can't serialize action in emit"); unsafe { - let (ptr, len) = from_i64(raw_retrieve_action(ret.as_ptr(), ret.len())); - let a = ::std::slice::from_raw_parts(ptr as _, len as _); + let ptr = raw_retrieve_action(to_i64(ret.as_ptr() as _), to_i64(ret.len() as _)); + let ptr = from_i64(ptr); + let len = + u64::from_le_bytes(std::slice::from_raw_parts(ptr as _, 8).try_into().unwrap()); + let a = ::std::slice::from_raw_parts((ptr + 8) as _, len as _); bincode::deserialize::>(&a) .map_err(|x| RetrieveError::BincodeError(x.to_string()))? } @@ -45,36 +48,50 @@ pub fn emit_actions(_actions: Vec) { { let ret = bincode::serialize(&_actions).expect("Can't serialize action in emit"); unsafe { - raw_emit_actions(ret.as_ptr(), ret.len()); + raw_emit_actions(to_i64(ret.as_ptr() as _), to_i64(ret.len() as _)); } } } -pub fn read_input(ptr: i32, len: u32) -> Result +pub fn read_input(ptr: i64, len: i64) -> Result where T: DeserializeOwned, { - let slice = unsafe { ::std::slice::from_raw_parts(ptr as _, len as _) }; + let slice = unsafe { ::std::slice::from_raw_parts(from_i64(ptr) as _, from_i64(len) as _) }; bincode::deserialize(slice).map_err(|_| "Failed to deserialize function input") } -pub fn from_i64(i: i64) -> (i32, i32) { +pub fn from_u128(i: u128) -> (u64, u64) { let i = i.to_le_bytes(); ( - i32::from_le_bytes(i[0..4].try_into().unwrap()), - i32::from_le_bytes(i[4..8].try_into().unwrap()), + u64::from_le_bytes(i[0..8].try_into().unwrap()), + u64::from_le_bytes(i[8..16].try_into().unwrap()), ) } -pub fn to_i64(a: i32, b: i32) -> i64 { +pub fn to_u128(a: u64, b: u64) -> u128 { let a = a.to_le_bytes(); let b = b.to_le_bytes(); - i64::from_le_bytes([a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3]]) + u128::from_le_bytes([a, b].concat().try_into().unwrap()) } +pub fn to_i64(i: u64) -> i64 { i64::from_le_bytes(i.to_le_bytes()) } + +pub fn from_i64(i: i64) -> u64 { u64::from_le_bytes(i.to_le_bytes()) } + +static mut VEC: Vec = vec![]; +static mut DATA: Vec = vec![]; + pub fn write_output(value: impl Serialize) -> i64 { - let ret = bincode::serialize(&value).expect("Can't serialize event output"); - to_i64(ret.as_ptr() as _, ret.len() as _) + unsafe { + VEC = bincode::serialize(&value).expect("Can't serialize event output"); + DATA = [ + (VEC.as_ptr() as u64).to_le_bytes(), + (VEC.len() as u64).to_le_bytes(), + ] + .concat(); + to_i64(DATA.as_ptr() as u64) + } } static mut BUFFERS: Vec = Vec::new(); @@ -83,7 +100,7 @@ static mut BUFFERS: Vec = Vec::new(); /// # Safety /// This function should never be used only intented to by used by the host #[no_mangle] -pub unsafe fn wasm_prepare_buffer(size: i32) -> i32 { +pub unsafe fn wasm_prepare_buffer(size: i32) -> i64 { BUFFERS = vec![0u8; size as usize]; - BUFFERS.as_ptr() as i32 + BUFFERS.as_ptr() as i64 } diff --git a/server/src/lib.rs b/server/src/lib.rs index 089133d7bd..2f049e44c8 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -507,7 +507,7 @@ impl Server { // (e.g. run before controller system) //TODO: run in parallel sys::msg::general::Sys.run_now(&self.state.ecs()); - sys::msg::register::Sys.run_now(&self.state.ecs()); + sys::msg::register::register_run(self.state_mut().ecs_mut()); sys::msg::character_screen::Sys.run_now(&self.state.ecs()); sys::msg::in_game::Sys.run_now(&self.state.ecs()); sys::msg::ping::Sys.run_now(&self.state.ecs()); diff --git a/server/src/login_provider.rs b/server/src/login_provider.rs index 67c5aaaa7f..a4205ff28f 100644 --- a/server/src/login_provider.rs +++ b/server/src/login_provider.rs @@ -1,7 +1,10 @@ use crate::settings::BanRecord; use authc::{AuthClient, AuthClientError, AuthToken, Uuid}; use common_net::msg::RegisterError; +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}; @@ -53,6 +56,8 @@ impl LoginProvider { pub fn try_login( &mut self, username_or_token: &str, + world: &World, + plugin_manager: &PluginMgr, admins: &HashSet, whitelist: &HashSet, banlist: &HashMap, @@ -74,6 +79,22 @@ impl LoginProvider { return Err(RegisterError::NotOnWhitelist); } + match plugin_manager.execute_event(&world, "on_join", &PlayerJoinEvent { + player_name: username.clone(), + player_id: uuid.as_bytes().clone(), + }) { + Ok(e) => { + for i in e.into_iter() { + if let PlayerJoinResult::CloseConnection(a) = i { + return Err(RegisterError::KickedByPlugin(a)); + } + } + }, + Err(e) => { + error!("Error occured while executing `on_join`: {:?}",e); + }, + }; + // add the user to self.accounts self.login(uuid, username.clone())?; diff --git a/server/src/sys/msg/register.rs b/server/src/sys/msg/register.rs index e433f4d4ef..fc807662d6 100644 --- a/server/src/sys/msg/register.rs +++ b/server/src/sys/msg/register.rs @@ -1,4 +1,3 @@ -use super::super::SysTimer; use crate::{ client::Client, login_provider::LoginProvider, metrics::PlayerMetrics, EditableSettings, }; @@ -11,162 +10,151 @@ use common_net::msg::{ CharacterInfo, ClientRegister, PlayerInfo, PlayerListUpdate, RegisterError, ServerGeneral, ServerRegisterAnswer, }; +use common_sys::plugin::PluginMgr; use hashbrown::HashMap; -use specs::{Entities, Join, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage}; +use specs::{ + shred::{Fetch, FetchMut}, + Join, World, WorldExt, WriteStorage, +}; -impl Sys { - #[allow(clippy::too_many_arguments)] - fn handle_register_msg( - player_list: &HashMap, - new_players: &mut Vec, - entity: specs::Entity, - client: &Client, - player_metrics: &ReadExpect<'_, PlayerMetrics>, - login_provider: &mut WriteExpect<'_, LoginProvider>, - admins: &mut WriteStorage<'_, Admin>, - players: &mut WriteStorage<'_, Player>, - editable_settings: &ReadExpect<'_, EditableSettings>, - msg: ClientRegister, - ) -> Result<(), crate::error::Error> { - let (username, uuid) = match login_provider.try_login( - &msg.token_or_username, - &*editable_settings.admins, - &*editable_settings.whitelist, - &*editable_settings.banlist, - ) { - Err(err) => { - client.send(ServerRegisterAnswer::Err(err))?; - return Ok(()); - }, - Ok((username, uuid)) => (username, uuid), - }; - - let player = Player::new(username, uuid); - let is_admin = editable_settings.admins.contains(&uuid); - - if !player.is_valid() { - // Invalid player - client.send(ServerRegisterAnswer::Err(RegisterError::InvalidCharacter))?; +#[allow(clippy::too_many_arguments)] +fn handle_register_msg( + world: &World, + player_list: &HashMap, + new_players: &mut Vec, + entity: specs::Entity, + client: &Client, + player_metrics: &Fetch<'_, PlayerMetrics>, + login_provider: &mut FetchMut<'_, LoginProvider>, + admins: &mut WriteStorage<'_, Admin>, + players: &mut WriteStorage<'_, Player>, + editable_settings: &Fetch<'_, EditableSettings>, + msg: ClientRegister, +) -> Result<(), crate::error::Error> { + let plugin_mgr = world.read_resource::(); + let (username, uuid) = match login_provider.try_login( + &msg.token_or_username, + world, + &plugin_mgr, + &*editable_settings.admins, + &*editable_settings.whitelist, + &*editable_settings.banlist, + ) { + Err(err) => { + client.send(ServerRegisterAnswer::Err(err))?; return Ok(()); - } + }, + Ok((username, uuid)) => (username, uuid), + }; - if !players.contains(entity) { - // Add Player component to this client - let _ = players.insert(entity, player); - player_metrics.players_connected.inc(); + let player = Player::new(username, uuid); + let is_admin = editable_settings.admins.contains(&uuid); - // Give the Admin component to the player if their name exists in - // admin list - if is_admin { - let _ = admins.insert(entity, Admin); - } - - // Tell the client its request was successful. - client.send(ServerRegisterAnswer::Ok(()))?; - - // Send initial player list - client.send(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init( - player_list.clone(), - )))?; - - // Add to list to notify all clients of the new player - new_players.push(entity); - } - - Ok(()) + if !player.is_valid() { + // Invalid player + client.send(ServerRegisterAnswer::Err(RegisterError::InvalidCharacter))?; + return Ok(()); } + + if !players.contains(entity) { + // Add Player component to this client + let _ = players.insert(entity, player); + player_metrics.players_connected.inc(); + + // Give the Admin component to the player if their name exists in + // admin list + if is_admin { + let _ = admins.insert(entity, Admin); + } + + // Tell the client its request was successful. + client.send(ServerRegisterAnswer::Ok(()))?; + + // Send initial player list + client.send(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init( + player_list.clone(), + )))?; + + // Add to list to notify all clients of the new player + new_players.push(entity); + } + + Ok(()) } /// This system will handle new messages from clients pub struct Sys; -impl<'a> System<'a> for Sys { - #[allow(clippy::type_complexity)] - type SystemData = ( - Entities<'a>, - ReadExpect<'a, PlayerMetrics>, - Write<'a, SysTimer>, - ReadStorage<'a, Uid>, - ReadStorage<'a, Client>, - WriteStorage<'a, Player>, - ReadStorage<'a, Stats>, - WriteExpect<'a, LoginProvider>, - WriteStorage<'a, Admin>, - ReadExpect<'a, EditableSettings>, - ); - fn run( - &mut self, - ( - entities, - player_metrics, - mut timer, - uids, - clients, - mut players, - stats, - mut login_provider, - mut admins, - editable_settings, - ): Self::SystemData, - ) { - span!(_guard, "run", "msg::register::Sys::run"); - timer.start(); +pub fn register_run(world: &mut World) { + let entities = world.entities(); + let player_metrics = world.read_resource::(); + //let mut timer = world.write_resource::>(); + let uids = world.read_storage::(); + let clients = world.read_storage::(); + let mut players = world.write_storage::(); + let stats = world.read_storage::(); + let mut login_provider = world.write_resource::(); + let mut admins = world.write_storage::(); + let editable_settings = world.read_resource::(); - // Player list to send new players. - let player_list = (&uids, &players, stats.maybe(), admins.maybe()) - .join() - .map(|(uid, player, stats, admin)| { - (*uid, PlayerInfo { - is_online: true, - is_admin: admin.is_some(), - player_alias: player.alias.clone(), - character: stats.map(|stats| CharacterInfo { - name: stats.name.clone(), - }), - }) + span!(_guard, "run", "msg::register::Sys::run"); + //timer.start(); + + // Player list to send new players. + let player_list = (&uids, &players, stats.maybe(), admins.maybe()) + .join() + .map(|(uid, player, stats, admin)| { + (*uid, PlayerInfo { + is_online: true, + is_admin: admin.is_some(), + player_alias: player.alias.clone(), + character: stats.map(|stats| CharacterInfo { + name: stats.name.clone(), + }), }) - .collect::>(); - // List of new players to update player lists of all clients. - let mut new_players = Vec::new(); + }) + .collect::>(); + // List of new players to update player lists of all clients. + let mut new_players = Vec::new(); - for (entity, client) in (&entities, &clients).join() { - let _ = super::try_recv_all(client, 0, |client, msg| { - Self::handle_register_msg( - &player_list, - &mut new_players, - entity, - client, - &player_metrics, - &mut login_provider, - &mut admins, - &mut players, - &editable_settings, - msg, - ) - }); - } + for (entity, client) in (&entities, &clients).join() { + let _ = super::try_recv_all(client, 0, |client, msg| { + handle_register_msg( + &world, + &player_list, + &mut new_players, + entity, + client, + &player_metrics, + &mut login_provider, + &mut admins, + &mut players, + &editable_settings, + msg, + ) + }); + } - // Handle new players. - // Tell all clients to add them to the player list. - for entity in new_players { - if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) { - let mut lazy_msg = None; - for (_, client) in (&players, &clients).join() { - if lazy_msg.is_none() { - lazy_msg = Some(client.prepare(ServerGeneral::PlayerListUpdate( - PlayerListUpdate::Add(*uid, PlayerInfo { - player_alias: player.alias.clone(), - is_online: true, - is_admin: admins.get(entity).is_some(), - character: None, // new players will be on character select. - }), - ))); - } - lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg)); + // Handle new players. + // Tell all clients to add them to the player list. + for entity in new_players { + if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) { + let mut lazy_msg = None; + for (_, client) in (&players, &clients).join() { + if lazy_msg.is_none() { + lazy_msg = Some(client.prepare(ServerGeneral::PlayerListUpdate( + PlayerListUpdate::Add(*uid, PlayerInfo { + player_alias: player.alias.clone(), + is_online: true, + is_admin: admins.get(entity).is_some(), + character: None, // new players will be on character select. + }), + ))); } + lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg)); } } - - timer.end() } + + //timer.end() } diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 6cc4df7ff0..b5ee1da042 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -134,6 +134,7 @@ impl PlayState for MainMenuState { localized_strings.get("main.login.authentication_error"), e ), + client::Error::KickedByPlugin(e) => e, client::Error::TooManyPlayers => { localized_strings.get("main.login.server_full").into() },