Lots of improvement in pointer management (switched from i32 to u64) + New event implemented

This commit is contained in:
ccgauche 2021-03-01 19:00:44 +01:00
parent b7bd0306e6
commit dbd4d70b79
15 changed files with 281 additions and 200 deletions

View File

@ -4,6 +4,7 @@ use network::{ParticipantError, StreamError};
#[derive(Debug)]
pub enum Error {
KickedByPlugin(String),
NetworkErr(NetworkError),
ParticipantErr(ParticipantError),
StreamErr(StreamError),

View File

@ -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;

View File

@ -191,6 +191,7 @@ pub enum RegisterError {
AlreadyLoggedIn,
AuthError(String),
Banned(String),
KickedByPlugin(String),
InvalidCharacter,
NotOnWhitelist,
//TODO: InvalidAlias,

View File

@ -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;

View File

@ -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<i32, MemoryAllocationError> {
) -> Result<u64, MemoryAllocationError> {
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<T: Serialize>(
&self,
memory: &Memory,
allocator: &Function,
object: &T,
) -> Result<u64, PluginModuleError> {
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<u64, PluginModuleError> {
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<T: DeserializeOwned>(
memory: &Memory,
position: i32,
length: u32,
position: u64,
length: u64,
) -> Result<T, bincode::Error> {
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<u8> {
pub fn read_bytes(memory: &Memory, position: u64, length: u64) -> Vec<u8> {
memory.view()[(position as usize)..(position as usize) + length as usize]
.iter()
.map(|x| x.get())

View File

@ -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<T: Event> PreparedEventQuery<T> {
}
}
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()),
))
}

View File

@ -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<T: Serialize>(&self, object: &T) -> Result<(i32, u32), PluginModuleError> {
pub fn write_data<T: Serialize>(&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<T: Serialize>(
&self,
object: &T,
) -> Result<u64, PluginModuleError> {
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<T: DeserializeOwned>(
&self,
position: i32,
length: u32,
position: u64,
length: u64,
) -> Result<T, bincode::Error> {
memory_manager::read_data(self.memory.get_ref().unwrap(), position, length)
}

View File

@ -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,
}

View File

@ -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

View File

@ -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<Vec<String>, 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
}

View File

@ -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<T: DeserializeOwned>(_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::<Result<T, RetrieveError>>(&a)
.map_err(|x| RetrieveError::BincodeError(x.to_string()))?
}
@ -45,36 +48,50 @@ pub fn emit_actions(_actions: Vec<api::Action>) {
{
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<T>(ptr: i32, len: u32) -> Result<T, &'static str>
pub fn read_input<T>(ptr: i64, len: i64) -> Result<T, &'static str>
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<u8> = vec![];
static mut DATA: Vec<u8> = 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<u8> = Vec::new();
@ -83,7 +100,7 @@ static mut BUFFERS: Vec<u8> = 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
}

View File

@ -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());

View File

@ -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<Uuid>,
whitelist: &HashSet<Uuid>,
banlist: &HashMap<Uuid, BanRecord>,
@ -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())?;

View File

@ -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<Uid, PlayerInfo>,
new_players: &mut Vec<specs::Entity>,
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<Uid, PlayerInfo>,
new_players: &mut Vec<specs::Entity>,
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::<PluginMgr>();
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<Self>>,
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::<PlayerMetrics>();
//let mut timer = world.write_resource::<SysTimer<Self>>();
let uids = world.read_storage::<Uid>();
let clients = world.read_storage::<Client>();
let mut players = world.write_storage::<Player>();
let stats = world.read_storage::<Stats>();
let mut login_provider = world.write_resource::<LoginProvider>();
let mut admins = world.write_storage::<Admin>();
let editable_settings = world.read_resource::<EditableSettings>();
// 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::<HashMap<_, _>>();
// List of new players to update player lists of all clients.
let mut new_players = Vec::new();
})
.collect::<HashMap<_, _>>();
// 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()
}

View File

@ -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()
},