mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Redo Network Frontend.
Rather than having a single Stream to handle ALL data, seperate into multiple streams: - Ping Stream, for seperate PINGS - Register Stream, only used till the client is registered, then no longer used! - General Stream, used for msg that can occur always - NotInGame Stream, used for everything NOT ingame, e.g. Character Screen - InGame Stream, used for all GAME data, players, terrain, entities, etc... This version does compile, and gets the client registered (with auth too) but doesnt get to the char screen yet. This fixes also the ignoring messages problem we had, as we are not sending data to the register stream! This fixes also the problem that the server had to sleep for the Stream Creation, as the Server is now creating the streams and client has to sleep.
This commit is contained in:
parent
8bd1f763e0
commit
b1db5ef488
1276
client/src/lib.rs
1276
client/src/lib.rs
File diff suppressed because it is too large
Load Diff
@ -7,13 +7,26 @@ use crate::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vek::*;
|
||||
|
||||
/// Messages sent from the client to the server
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ClientMsg {
|
||||
Register {
|
||||
view_distance: Option<u32>,
|
||||
token_or_username: String,
|
||||
},
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ClientType {
|
||||
// Regular Client like Voxygen who plays the game
|
||||
Game,
|
||||
// A Chatonly client, which doesn't want to connect via its character
|
||||
ChatOnly,
|
||||
// A unprivileged bot, e.g. to request world information
|
||||
// Or a privileged bot, e.g. to run admin commands used by server-cli
|
||||
Bot { privileged: bool },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ClientRegisterMsg {
|
||||
pub view_distance: Option<u32>,
|
||||
pub token_or_username: String,
|
||||
}
|
||||
|
||||
//messages send by clients only valid when NOT ingame
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ClientNotInGameMsg {
|
||||
RequestCharacterList,
|
||||
CreateCharacter {
|
||||
alias: String,
|
||||
@ -22,20 +35,19 @@ pub enum ClientMsg {
|
||||
},
|
||||
DeleteCharacter(CharacterId),
|
||||
Character(CharacterId),
|
||||
/// Request `ClientState::Registered` from an ingame state
|
||||
ExitIngame,
|
||||
/// Request `ClientState::Spectator` from a registered or ingame state
|
||||
Spectate,
|
||||
}
|
||||
|
||||
//messages send by clients only valid when ingame
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ClientInGameMsg {
|
||||
ControllerInputs(comp::ControllerInputs),
|
||||
ControlEvent(comp::ControlEvent),
|
||||
ControlAction(comp::ControlAction),
|
||||
SetViewDistance(u32),
|
||||
BreakBlock(Vec3<i32>),
|
||||
PlaceBlock(Vec3<i32>, Block),
|
||||
Ping,
|
||||
Pong,
|
||||
/// Send the chat message or command to be processed by the server
|
||||
ChatMsg(String),
|
||||
ExitInGame,
|
||||
PlayerPhysics {
|
||||
pos: comp::Pos,
|
||||
vel: comp::Vel,
|
||||
@ -44,9 +56,16 @@ pub enum ClientMsg {
|
||||
TerrainChunkRequest {
|
||||
key: Vec2<i32>,
|
||||
},
|
||||
Disconnect,
|
||||
Terminate,
|
||||
UnlockSkill(Skill),
|
||||
RefundSkill(Skill),
|
||||
UnlockSkillGroup(SkillGroupType),
|
||||
}
|
||||
|
||||
/// Messages sent from the client to the server
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ClientMsg {
|
||||
ChatMsg(String),
|
||||
Command(String),
|
||||
Disconnect,
|
||||
Terminate,
|
||||
}
|
||||
|
@ -4,24 +4,28 @@ pub mod server;
|
||||
|
||||
// Reexports
|
||||
pub use self::{
|
||||
client::ClientMsg,
|
||||
client::{ClientInGameMsg, ClientMsg, ClientNotInGameMsg, ClientRegisterMsg, ClientType},
|
||||
ecs_packet::EcsCompPacket,
|
||||
server::{
|
||||
CharacterInfo, DisconnectReason, InviteAnswer, Notification, PlayerInfo, PlayerListUpdate,
|
||||
RegisterError, RequestStateError, ServerInfo, ServerMsg,
|
||||
RegisterError, ServerInGameMsg, ServerInfo, ServerInitMsg, ServerMsg, ServerNotInGameMsg,
|
||||
ServerRegisterAnswerMsg,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ClientState {
|
||||
Pending,
|
||||
Connected,
|
||||
Registered,
|
||||
pub enum ClientIngame {
|
||||
Spectator,
|
||||
Character,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum PingMsg {
|
||||
Ping,
|
||||
Pong,
|
||||
}
|
||||
|
||||
pub const MAX_BYTES_CHAT_MSG: usize = 256;
|
||||
|
||||
pub enum ChatMsgValidationError {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{ClientState, EcsCompPacket};
|
||||
use super::EcsCompPacket;
|
||||
use crate::{
|
||||
character::CharacterItem,
|
||||
comp,
|
||||
@ -192,25 +192,37 @@ pub enum DisconnectReason {
|
||||
Kicked(String),
|
||||
}
|
||||
|
||||
/// Messages sent from the server to the client
|
||||
/// Reponse To ClientType
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerMsg {
|
||||
InitialSync {
|
||||
pub enum ServerInitMsg {
|
||||
TooManyPlayers,
|
||||
GameSync {
|
||||
entity_package: sync::EntityPackage<EcsCompPacket>,
|
||||
server_info: ServerInfo,
|
||||
time_of_day: state::TimeOfDay,
|
||||
max_group_size: u32,
|
||||
client_timeout: Duration,
|
||||
world_map: WorldMapMsg,
|
||||
recipe_book: RecipeBook,
|
||||
},
|
||||
}
|
||||
|
||||
pub type ServerRegisterAnswerMsg = Result<(), RegisterError>;
|
||||
|
||||
//Messages only allowed while client ingame
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerNotInGameMsg {
|
||||
/// An error occurred while loading character data
|
||||
CharacterDataLoadError(String),
|
||||
/// A list of characters belonging to the a authenticated player was sent
|
||||
CharacterListUpdate(Vec<CharacterItem>),
|
||||
/// An error occurred while creating or deleting a character
|
||||
CharacterActionError(String),
|
||||
PlayerListUpdate(PlayerListUpdate),
|
||||
CharacterSuccess,
|
||||
}
|
||||
|
||||
//Messages only allowed while client ingame
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerInGameMsg {
|
||||
GroupUpdate(comp::group::ChangeNotification<sync::Uid>),
|
||||
// Indicate to the client that they are invited to join a group
|
||||
GroupInvite {
|
||||
@ -227,12 +239,24 @@ pub enum ServerMsg {
|
||||
target: sync::Uid,
|
||||
answer: InviteAnswer,
|
||||
},
|
||||
StateAnswer(Result<ClientState, (RequestStateError, ClientState)>),
|
||||
/// Trigger cleanup for when the client goes back to the `Registered` state
|
||||
/// from an ingame state
|
||||
ExitIngameCleanup,
|
||||
Ping,
|
||||
Pong,
|
||||
ExitInGameSuccess,
|
||||
InventoryUpdate(comp::Inventory, comp::InventoryUpdateEvent),
|
||||
TerrainChunkUpdate {
|
||||
key: Vec2<i32>,
|
||||
chunk: Result<Box<TerrainChunk>, ()>,
|
||||
},
|
||||
TerrainBlockUpdates(HashMap<Vec3<i32>, Block>),
|
||||
SetViewDistance(u32),
|
||||
Outcomes(Vec<Outcome>),
|
||||
Knockback(Vec3<f32>),
|
||||
}
|
||||
|
||||
/// Messages sent from the server to the client
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerMsg {
|
||||
PlayerListUpdate(PlayerListUpdate),
|
||||
/// A message to go into the client chat box. The client is responsible for
|
||||
/// formatting the message and turning it into a speech bubble.
|
||||
ChatMsg(comp::ChatMsg),
|
||||
@ -242,28 +266,9 @@ pub enum ServerMsg {
|
||||
CompSync(sync::CompSyncPackage<EcsCompPacket>),
|
||||
CreateEntity(sync::EntityPackage<EcsCompPacket>),
|
||||
DeleteEntity(Uid),
|
||||
InventoryUpdate(comp::Inventory, comp::InventoryUpdateEvent),
|
||||
TerrainChunkUpdate {
|
||||
key: Vec2<i32>,
|
||||
chunk: Result<Box<TerrainChunk>, ()>,
|
||||
},
|
||||
TerrainBlockUpdates(HashMap<Vec3<i32>, Block>),
|
||||
Disconnect(DisconnectReason),
|
||||
TooManyPlayers,
|
||||
/// Send a popup notification such as "Waypoint Saved"
|
||||
Notification(Notification),
|
||||
SetViewDistance(u32),
|
||||
Outcomes(Vec<Outcome>),
|
||||
Knockback(Vec3<f32>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum RequestStateError {
|
||||
RegisterDenied(RegisterError),
|
||||
Denied,
|
||||
Already,
|
||||
Impossible,
|
||||
WrongMessage,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
|
@ -1,20 +1,30 @@
|
||||
use crate::error::Error;
|
||||
use common::msg::{ClientMsg, ClientState, RequestStateError, ServerMsg};
|
||||
use common::msg::{
|
||||
ClientInGameMsg, ClientIngame, ClientMsg, ClientNotInGameMsg, ClientType, PingMsg,
|
||||
ServerInGameMsg, ServerInitMsg, ServerMsg, ServerNotInGameMsg,
|
||||
};
|
||||
use hashbrown::HashSet;
|
||||
use network::{Participant, Stream};
|
||||
use network::{MessageBuffer, Participant, Stream};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use specs::{Component, FlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Mutex,
|
||||
Arc, Mutex,
|
||||
};
|
||||
use tracing::debug;
|
||||
use vek::*;
|
||||
|
||||
pub struct Client {
|
||||
pub client_state: ClientState,
|
||||
pub registered: bool,
|
||||
pub client_type: ClientType,
|
||||
pub in_game: Option<ClientIngame>,
|
||||
pub participant: Mutex<Option<Participant>>,
|
||||
pub singleton_stream: Stream,
|
||||
pub ping_stream: Stream,
|
||||
pub register_stream: Stream,
|
||||
pub in_game_stream: Stream,
|
||||
pub not_in_game_stream: Stream,
|
||||
pub network_error: AtomicBool,
|
||||
pub last_ping: f64,
|
||||
pub login_msg_sent: bool,
|
||||
@ -25,22 +35,58 @@ impl Component for Client {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn notify(&mut self, msg: ServerMsg) {
|
||||
if !self.network_error.load(Ordering::Relaxed) {
|
||||
if let Err(e) = self.singleton_stream.send(msg) {
|
||||
fn internal_send<M: Serialize>(b: &AtomicBool, s: &mut Stream, msg: M) {
|
||||
if !b.load(Ordering::Relaxed) {
|
||||
if let Err(e) = s.send(msg) {
|
||||
debug!(?e, "got a network error with client");
|
||||
self.network_error.store(true, Ordering::Relaxed);
|
||||
b.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn recv(&mut self) -> Result<ClientMsg, Error> {
|
||||
if !self.network_error.load(Ordering::Relaxed) {
|
||||
match self.singleton_stream.recv().await {
|
||||
fn internal_send_raw(b: &AtomicBool, s: &mut Stream, msg: Arc<MessageBuffer>) {
|
||||
if !b.load(Ordering::Relaxed) {
|
||||
if let Err(e) = s.send_raw(msg) {
|
||||
debug!(?e, "got a network error with client");
|
||||
b.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_init(&mut self, msg: ServerInitMsg) {
|
||||
Self::internal_send(&self.network_error, &mut self.register_stream, msg);
|
||||
}
|
||||
|
||||
pub fn send_msg(&mut self, msg: ServerMsg) {
|
||||
Self::internal_send(&self.network_error, &mut self.singleton_stream, msg);
|
||||
}
|
||||
|
||||
pub fn send_in_game(&mut self, msg: ServerInGameMsg) {
|
||||
Self::internal_send(&self.network_error, &mut self.in_game_stream, msg);
|
||||
}
|
||||
|
||||
pub fn send_not_in_game(&mut self, msg: ServerNotInGameMsg) {
|
||||
Self::internal_send(&self.network_error, &mut self.not_in_game_stream, msg);
|
||||
}
|
||||
|
||||
pub fn send_ping(&mut self, msg: PingMsg) {
|
||||
Self::internal_send(&self.network_error, &mut self.ping_stream, msg);
|
||||
}
|
||||
|
||||
pub fn send_msg_raw(&mut self, msg: Arc<MessageBuffer>) {
|
||||
Self::internal_send_raw(&self.network_error, &mut self.singleton_stream, msg);
|
||||
}
|
||||
|
||||
pub async fn internal_recv<M: DeserializeOwned>(
|
||||
b: &AtomicBool,
|
||||
s: &mut Stream,
|
||||
) -> Result<M, Error> {
|
||||
if !b.load(Ordering::Relaxed) {
|
||||
match s.recv().await {
|
||||
Ok(r) => Ok(r),
|
||||
Err(e) => {
|
||||
debug!(?e, "got a network error with client while recv");
|
||||
self.network_error.store(true, Ordering::Relaxed);
|
||||
b.store(true, Ordering::Relaxed);
|
||||
Err(Error::StreamErr(e))
|
||||
},
|
||||
}
|
||||
@ -49,29 +95,20 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_registered(&self) -> bool {
|
||||
matches!(
|
||||
self.client_state,
|
||||
ClientState::Registered | ClientState::Spectator | ClientState::Character
|
||||
)
|
||||
pub async fn recv_msg(&mut self) -> Result<ClientMsg, Error> {
|
||||
Self::internal_recv(&self.network_error, &mut self.singleton_stream).await
|
||||
}
|
||||
|
||||
pub fn is_ingame(&self) -> bool {
|
||||
matches!(
|
||||
self.client_state,
|
||||
ClientState::Spectator | ClientState::Character
|
||||
)
|
||||
pub async fn recv_in_game_msg(&mut self) -> Result<ClientInGameMsg, Error> {
|
||||
Self::internal_recv(&self.network_error, &mut self.in_game_stream).await
|
||||
}
|
||||
|
||||
pub fn allow_state(&mut self, new_state: ClientState) {
|
||||
self.client_state = new_state;
|
||||
let _ = self
|
||||
.singleton_stream
|
||||
.send(ServerMsg::StateAnswer(Ok(new_state)));
|
||||
pub async fn recv_not_in_game_msg(&mut self) -> Result<ClientNotInGameMsg, Error> {
|
||||
Self::internal_recv(&self.network_error, &mut self.not_in_game_stream).await
|
||||
}
|
||||
|
||||
pub fn error_state(&mut self, error: RequestStateError) {
|
||||
let _ = self.notify(ServerMsg::StateAnswer(Err((error, self.client_state))));
|
||||
pub async fn recv_ping_msg(&mut self) -> Result<PingMsg, Error> {
|
||||
Self::internal_recv(&self.network_error, &mut self.ping_stream).await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ use common::{
|
||||
cmd::{ChatCommand, CHAT_COMMANDS, CHAT_SHORTCUTS},
|
||||
comp::{self, ChatType, Item, LightEmitter, WaypointArea},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{DisconnectReason, Notification, PlayerListUpdate, ServerMsg},
|
||||
msg::{DisconnectReason, Notification, PlayerListUpdate, ServerInGameMsg, ServerMsg},
|
||||
npc::{self, get_npc_name},
|
||||
state::TimeOfDay,
|
||||
sync::{Uid, WorldSyncExt},
|
||||
@ -667,7 +667,9 @@ fn handle_spawn(
|
||||
.try_map(|e| uids.get(e).copied())
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| c.notify(ServerMsg::GroupUpdate(g)));
|
||||
.map(|(g, c)| {
|
||||
c.send_in_game(ServerInGameMsg::GroupUpdate(g))
|
||||
});
|
||||
},
|
||||
);
|
||||
} else if let Some(group) = match alignment {
|
||||
|
@ -12,7 +12,7 @@ use common::{
|
||||
Player, Pos, Stats,
|
||||
},
|
||||
lottery::Lottery,
|
||||
msg::{PlayerListUpdate, ServerMsg},
|
||||
msg::{PlayerListUpdate, ServerInGameMsg, ServerMsg},
|
||||
outcome::Outcome,
|
||||
state::BlockChange,
|
||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
||||
@ -44,7 +44,7 @@ pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3<f32>)
|
||||
}
|
||||
let mut clients = state.ecs().write_storage::<Client>();
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(ServerMsg::Knockback(impulse));
|
||||
client.send_in_game(ServerInGameMsg::Knockback(impulse));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use common::{
|
||||
group::{self, Group, GroupManager, Invite, PendingInvites},
|
||||
ChatType, GroupManip,
|
||||
},
|
||||
msg::{InviteAnswer, ServerMsg},
|
||||
msg::{InviteAnswer, ServerInGameMsg},
|
||||
sync,
|
||||
sync::WorldSyncExt,
|
||||
};
|
||||
@ -31,7 +31,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
None => {
|
||||
// Inform of failure
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta
|
||||
.server_msg("Invite failed, target does not exist.".to_owned()),
|
||||
);
|
||||
@ -63,7 +63,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
if already_in_same_group {
|
||||
// Inform of failure
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(ChatType::Meta.server_msg(
|
||||
client.send_msg(ChatType::Meta.server_msg(
|
||||
"Invite failed, can't invite someone already in your group".to_owned(),
|
||||
));
|
||||
}
|
||||
@ -93,7 +93,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
if group_size_limit_reached {
|
||||
// Inform inviter that they have reached the group size limit
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta.server_msg(
|
||||
"Invite failed, pending invites plus current group size have reached \
|
||||
the group size limit"
|
||||
@ -110,7 +110,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
if invites.contains(invitee) {
|
||||
// Inform inviter that there is already an invite
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta
|
||||
.server_msg("This player already has a pending invite.".to_owned()),
|
||||
);
|
||||
@ -155,7 +155,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
(clients.get_mut(invitee), uids.get(entity).copied())
|
||||
{
|
||||
if send_invite() {
|
||||
client.notify(ServerMsg::GroupInvite {
|
||||
client.send_in_game(ServerInGameMsg::GroupInvite {
|
||||
inviter,
|
||||
timeout: PRESENTED_INVITE_TIMEOUT_DUR,
|
||||
});
|
||||
@ -163,7 +163,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
} else if agents.contains(invitee) {
|
||||
send_invite();
|
||||
} else if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta.server_msg("Can't invite, not a player or npc".to_owned()),
|
||||
);
|
||||
}
|
||||
@ -171,7 +171,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
// Notify inviter that the invite is pending
|
||||
if invite_sent {
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(ServerMsg::InvitePending(uid));
|
||||
client.send_in_game(ServerInGameMsg::InvitePending(uid));
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -196,7 +196,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
if let (Some(client), Some(target)) =
|
||||
(clients.get_mut(inviter), uids.get(entity).copied())
|
||||
{
|
||||
client.notify(ServerMsg::InviteComplete {
|
||||
client.send_in_game(ServerInGameMsg::InviteComplete {
|
||||
target,
|
||||
answer: InviteAnswer::Accepted,
|
||||
})
|
||||
@ -217,7 +217,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
.try_map(|e| uids.get(e).copied())
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| c.notify(ServerMsg::GroupUpdate(g)));
|
||||
.map(|(g, c)| c.send_in_game(ServerInGameMsg::GroupUpdate(g)));
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -244,7 +244,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
if let (Some(client), Some(target)) =
|
||||
(clients.get_mut(inviter), uids.get(entity).copied())
|
||||
{
|
||||
client.notify(ServerMsg::InviteComplete {
|
||||
client.send_in_game(ServerInGameMsg::InviteComplete {
|
||||
target,
|
||||
answer: InviteAnswer::Declined,
|
||||
})
|
||||
@ -269,7 +269,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
.try_map(|e| uids.get(e).copied())
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| c.notify(ServerMsg::GroupUpdate(g)));
|
||||
.map(|(g, c)| c.send_in_game(ServerInGameMsg::GroupUpdate(g)));
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -283,7 +283,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
None => {
|
||||
// Inform of failure
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta
|
||||
.server_msg("Kick failed, target does not exist.".to_owned()),
|
||||
);
|
||||
@ -296,7 +296,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
if matches!(alignments.get(target), Some(comp::Alignment::Owned(owner)) if uids.get(target).map_or(true, |u| u != owner))
|
||||
{
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta.server_msg("Kick failed, you can't kick pets.".to_owned()),
|
||||
);
|
||||
}
|
||||
@ -305,7 +305,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
// Can't kick yourself
|
||||
if uids.get(entity).map_or(false, |u| *u == uid) {
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta
|
||||
.server_msg("Kick failed, you can't kick yourself.".to_owned()),
|
||||
);
|
||||
@ -336,26 +336,26 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
.try_map(|e| uids.get(e).copied())
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| c.notify(ServerMsg::GroupUpdate(g)));
|
||||
.map(|(g, c)| c.send_in_game(ServerInGameMsg::GroupUpdate(g)));
|
||||
},
|
||||
);
|
||||
|
||||
// Tell them the have been kicked
|
||||
if let Some(client) = clients.get_mut(target) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta
|
||||
.server_msg("You were removed from the group.".to_owned()),
|
||||
);
|
||||
}
|
||||
// Tell kicker that they were succesful
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(ChatType::Meta.server_msg("Player kicked.".to_owned()));
|
||||
client.send_msg(ChatType::Meta.server_msg("Player kicked.".to_owned()));
|
||||
}
|
||||
},
|
||||
Some(_) => {
|
||||
// Inform kicker that they are not the leader
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(ChatType::Meta.server_msg(
|
||||
client.send_msg(ChatType::Meta.server_msg(
|
||||
"Kick failed: You are not the leader of the target's group.".to_owned(),
|
||||
));
|
||||
}
|
||||
@ -363,7 +363,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
None => {
|
||||
// Inform kicker that the target is not in a group
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta.server_msg(
|
||||
"Kick failed: Your target is not in a group.".to_owned(),
|
||||
),
|
||||
@ -380,7 +380,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
None => {
|
||||
// Inform of failure
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(ChatType::Meta.server_msg(
|
||||
client.send_msg(ChatType::Meta.server_msg(
|
||||
"Leadership transfer failed, target does not exist".to_owned(),
|
||||
));
|
||||
}
|
||||
@ -410,18 +410,18 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
.try_map(|e| uids.get(e).copied())
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| c.notify(ServerMsg::GroupUpdate(g)));
|
||||
.map(|(g, c)| c.send_in_game(ServerInGameMsg::GroupUpdate(g)));
|
||||
},
|
||||
);
|
||||
// Tell them they are the leader
|
||||
if let Some(client) = clients.get_mut(target) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta.server_msg("You are the group leader now.".to_owned()),
|
||||
);
|
||||
}
|
||||
// Tell the old leader that the transfer was succesful
|
||||
if let Some(client) = clients.get_mut(target) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta
|
||||
.server_msg("You are no longer the group leader.".to_owned()),
|
||||
);
|
||||
@ -430,7 +430,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
Some(_) => {
|
||||
// Inform transferer that they are not the leader
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(
|
||||
client.send_msg(
|
||||
ChatType::Meta.server_msg(
|
||||
"Transfer failed: You are not the leader of the target's group."
|
||||
.to_owned(),
|
||||
@ -441,7 +441,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
||||
None => {
|
||||
// Inform transferer that the target is not in a group
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(ChatType::Meta.server_msg(
|
||||
client.send_msg(ChatType::Meta.server_msg(
|
||||
"Transfer failed: Your target is not in a group.".to_owned(),
|
||||
));
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) {
|
||||
let mut clients = ecs.write_storage::<Client>();
|
||||
if clients.get_mut(possesse).is_none() {
|
||||
if let Some(mut client) = clients.remove(possessor) {
|
||||
client.notify(ServerMsg::SetPlayerEntity(possesse_uid));
|
||||
client.send_msg(ServerMsg::SetPlayerEntity(possesse_uid));
|
||||
clients
|
||||
.insert(possesse, client)
|
||||
.err()
|
||||
|
@ -5,7 +5,7 @@ use common::{
|
||||
slot::{self, Slot},
|
||||
Pos, MAX_PICKUP_RANGE_SQR,
|
||||
},
|
||||
msg::ServerMsg,
|
||||
msg::ServerInGameMsg,
|
||||
recipe::default_recipe_book,
|
||||
sync::{Uid, WorldSyncExt},
|
||||
vol::ReadVol,
|
||||
@ -281,7 +281,9 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| {
|
||||
c.notify(ServerMsg::GroupUpdate(g))
|
||||
c.send_in_game(
|
||||
ServerInGameMsg::GroupUpdate(g),
|
||||
)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
use common::{
|
||||
comp,
|
||||
comp::{group, Player},
|
||||
msg::{ClientState, PlayerListUpdate, ServerMsg},
|
||||
msg::{PlayerListUpdate, ServerInGameMsg, ServerMsg},
|
||||
span,
|
||||
sync::{Uid, UidAllocator},
|
||||
};
|
||||
@ -33,9 +33,8 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) {
|
||||
.cloned();
|
||||
if let (Some(mut client), Some(uid), Some(player)) = (maybe_client, maybe_uid, maybe_player) {
|
||||
// Tell client its request was successful
|
||||
client.allow_state(ClientState::Registered);
|
||||
// Tell client to clear out other entities and its own components
|
||||
client.notify(ServerMsg::ExitIngameCleanup);
|
||||
client.in_game = None;
|
||||
client.send_in_game(ServerInGameMsg::ExitInGameSuccess);
|
||||
|
||||
let entity_builder = state.ecs_mut().create_entity().with(client).with(player);
|
||||
|
||||
|
@ -44,7 +44,10 @@ use common::{
|
||||
cmd::ChatCommand,
|
||||
comp::{self, ChatType},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{server::WorldMapMsg, ClientState, DisconnectReason, ServerInfo, ServerMsg},
|
||||
msg::{
|
||||
server::WorldMapMsg, ClientType, DisconnectReason, ServerInGameMsg, ServerInfo,
|
||||
ServerInitMsg, ServerMsg, ServerNotInGameMsg,
|
||||
},
|
||||
outcome::Outcome,
|
||||
recipe::default_recipe_book,
|
||||
state::{State, TimeOfDay},
|
||||
@ -56,7 +59,7 @@ use futures_executor::block_on;
|
||||
use futures_timer::Delay;
|
||||
use futures_util::{select, FutureExt};
|
||||
use metrics::{ServerMetrics, StateTickMetrics, TickMetrics};
|
||||
use network::{Network, Pid, ProtocolAddr};
|
||||
use network::{Network, Pid, Promises, ProtocolAddr};
|
||||
use persistence::{
|
||||
character_loader::{CharacterLoader, CharacterLoaderResponseType},
|
||||
character_updater::CharacterUpdater,
|
||||
@ -70,7 +73,7 @@ use std::{
|
||||
};
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
use test_world::{IndexOwned, World};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use tracing::{debug, error, info, trace};
|
||||
use uvth::{ThreadPool, ThreadPoolBuilder};
|
||||
use vek::*;
|
||||
#[cfg(feature = "worldgen")]
|
||||
@ -521,13 +524,13 @@ impl Server {
|
||||
.messages()
|
||||
.for_each(|query_result| match query_result.result {
|
||||
CharacterLoaderResponseType::CharacterList(result) => match result {
|
||||
Ok(character_list_data) => self.notify_client(
|
||||
Ok(character_list_data) => self.notify_not_in_game_client(
|
||||
query_result.entity,
|
||||
ServerMsg::CharacterListUpdate(character_list_data),
|
||||
ServerNotInGameMsg::CharacterListUpdate(character_list_data),
|
||||
),
|
||||
Err(error) => self.notify_client(
|
||||
Err(error) => self.notify_not_in_game_client(
|
||||
query_result.entity,
|
||||
ServerMsg::CharacterActionError(error.to_string()),
|
||||
ServerNotInGameMsg::CharacterActionError(error.to_string()),
|
||||
),
|
||||
},
|
||||
CharacterLoaderResponseType::CharacterData(result) => {
|
||||
@ -540,9 +543,9 @@ impl Server {
|
||||
// We failed to load data for the character from the DB. Notify the
|
||||
// client to push the state back to character selection, with the error
|
||||
// to display
|
||||
self.notify_client(
|
||||
self.notify_not_in_game_client(
|
||||
query_result.entity,
|
||||
ServerMsg::CharacterDataLoadError(error.to_string()),
|
||||
ServerNotInGameMsg::CharacterDataLoadError(error.to_string()),
|
||||
);
|
||||
|
||||
// Clean up the entity data on the server
|
||||
@ -797,7 +800,6 @@ impl Server {
|
||||
) -> Result<(), Error> {
|
||||
//TIMEOUT 0.1 ms for msg handling
|
||||
const TIMEOUT: Duration = Duration::from_micros(100);
|
||||
const SLOWLORIS_TIMEOUT: Duration = Duration::from_millis(300);
|
||||
loop {
|
||||
let participant = match select!(
|
||||
_ = Delay::new(TIMEOUT).fuse() => None,
|
||||
@ -807,71 +809,77 @@ impl Server {
|
||||
Some(pr) => pr?,
|
||||
};
|
||||
debug!("New Participant connected to the server");
|
||||
let reliable = Promises::ORDERED | Promises::CONSISTENCY;
|
||||
let reliablec = reliable | Promises::COMPRESSED;
|
||||
|
||||
let singleton_stream = match select!(
|
||||
_ = Delay::new(SLOWLORIS_TIMEOUT).fuse() => None,
|
||||
sr = participant.opened().fuse() => Some(sr),
|
||||
) {
|
||||
None => {
|
||||
warn!("Either Slowloris attack or very slow client, dropping");
|
||||
return Ok(()); //return rather then continue to give removes a tick more to send data.
|
||||
},
|
||||
Some(Ok(s)) => s,
|
||||
Some(Err(e)) => {
|
||||
warn!(?e, "Failed to open a Stream from remote client. dropping");
|
||||
continue;
|
||||
},
|
||||
};
|
||||
let stream = participant.open(10, reliablec).await?;
|
||||
let ping_stream = participant.open(5, reliable).await?;
|
||||
let mut register_stream = participant.open(10, reliablec).await?;
|
||||
let in_game_stream = participant.open(10, reliablec).await?;
|
||||
let not_in_game_stream = participant.open(10, reliablec).await?;
|
||||
|
||||
let mut client = Client {
|
||||
client_state: ClientState::Connected,
|
||||
register_stream.send(self.get_server_info())?;
|
||||
let client_type: ClientType = register_stream.recv().await?;
|
||||
|
||||
if self.settings().max_players
|
||||
<= self.state.ecs().read_storage::<Client>().join().count()
|
||||
{
|
||||
trace!(
|
||||
?participant,
|
||||
"to many players, wont allow participant to connect"
|
||||
);
|
||||
register_stream.send(ServerInitMsg::TooManyPlayers)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
let client = Client {
|
||||
registered: false,
|
||||
client_type,
|
||||
in_game: None,
|
||||
participant: std::sync::Mutex::new(Some(participant)),
|
||||
singleton_stream,
|
||||
singleton_stream: stream,
|
||||
ping_stream,
|
||||
register_stream,
|
||||
in_game_stream,
|
||||
not_in_game_stream,
|
||||
network_error: std::sync::atomic::AtomicBool::new(false),
|
||||
last_ping: self.state.get_time(),
|
||||
login_msg_sent: false,
|
||||
};
|
||||
|
||||
if self.settings().max_players
|
||||
<= self.state.ecs().read_storage::<Client>().join().count()
|
||||
{
|
||||
// Note: in this case the client is dropped
|
||||
client.notify(ServerMsg::TooManyPlayers);
|
||||
} else {
|
||||
let entity = self
|
||||
.state
|
||||
.ecs_mut()
|
||||
.create_entity_synced()
|
||||
.with(client)
|
||||
.build();
|
||||
self.state
|
||||
.ecs()
|
||||
.read_resource::<metrics::PlayerMetrics>()
|
||||
.clients_connected
|
||||
.inc();
|
||||
// Send client all the tracked components currently attached to its entity as
|
||||
// well as synced resources (currently only `TimeOfDay`)
|
||||
debug!("Starting initial sync with client.");
|
||||
self.state
|
||||
.ecs()
|
||||
.write_storage::<Client>()
|
||||
.get_mut(entity)
|
||||
.unwrap()
|
||||
.notify(ServerMsg::InitialSync {
|
||||
// Send client their entity
|
||||
entity_package: TrackedComps::fetch(&self.state.ecs())
|
||||
.create_entity_package(entity, None, None, None),
|
||||
server_info: self.get_server_info(),
|
||||
time_of_day: *self.state.ecs().read_resource(),
|
||||
max_group_size: self.settings().max_player_group_size,
|
||||
client_timeout: self.settings().client_timeout,
|
||||
world_map: self.map.clone(),
|
||||
recipe_book: (&*default_recipe_book()).clone(),
|
||||
});
|
||||
let entity = self
|
||||
.state
|
||||
.ecs_mut()
|
||||
.create_entity_synced()
|
||||
.with(client)
|
||||
.build();
|
||||
self.state
|
||||
.ecs()
|
||||
.read_resource::<metrics::PlayerMetrics>()
|
||||
.clients_connected
|
||||
.inc();
|
||||
// Send client all the tracked components currently attached to its entity as
|
||||
// well as synced resources (currently only `TimeOfDay`)
|
||||
debug!("Starting initial sync with client.");
|
||||
self.state
|
||||
.ecs()
|
||||
.write_storage::<Client>()
|
||||
.get_mut(entity)
|
||||
.unwrap()
|
||||
.register_stream
|
||||
.send(ServerInitMsg::GameSync {
|
||||
// Send client their entity
|
||||
entity_package: TrackedComps::fetch(&self.state.ecs())
|
||||
.create_entity_package(entity, None, None, None),
|
||||
time_of_day: *self.state.ecs().read_resource(),
|
||||
max_group_size: self.settings().max_player_group_size,
|
||||
client_timeout: self.settings().client_timeout,
|
||||
world_map: self.map.clone(),
|
||||
recipe_book: (&*default_recipe_book()).clone(),
|
||||
})?;
|
||||
|
||||
frontend_events.push(Event::ClientConnected { entity });
|
||||
debug!("Done initial sync with client.");
|
||||
}
|
||||
frontend_events.push(Event::ClientConnected { entity });
|
||||
debug!("Done initial sync with client.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -880,7 +888,25 @@ impl Server {
|
||||
S: Into<ServerMsg>,
|
||||
{
|
||||
if let Some(client) = self.state.ecs().write_storage::<Client>().get_mut(entity) {
|
||||
client.notify(msg.into())
|
||||
client.send_msg(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_in_game_client<S>(&self, entity: EcsEntity, msg: S)
|
||||
where
|
||||
S: Into<ServerInGameMsg>,
|
||||
{
|
||||
if let Some(client) = self.state.ecs().write_storage::<Client>().get_mut(entity) {
|
||||
client.send_in_game(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_not_in_game_client<S>(&self, entity: EcsEntity, msg: S)
|
||||
where
|
||||
S: Into<ServerNotInGameMsg>,
|
||||
{
|
||||
if let Some(client) = self.state.ecs().write_storage::<Client>().get_mut(entity) {
|
||||
client.send_not_in_game(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,10 @@ use common::{
|
||||
character::CharacterId,
|
||||
comp,
|
||||
effect::Effect,
|
||||
msg::{CharacterInfo, ClientState, PlayerListUpdate, ServerMsg},
|
||||
msg::{
|
||||
CharacterInfo, ClientIngame, PlayerListUpdate, ServerInGameMsg, ServerMsg,
|
||||
ServerNotInGameMsg,
|
||||
},
|
||||
state::State,
|
||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
||||
util::Dir,
|
||||
@ -216,7 +219,8 @@ impl StateExt for State {
|
||||
|
||||
// Tell the client its request was successful.
|
||||
if let Some(client) = self.ecs().write_storage::<Client>().get_mut(entity) {
|
||||
client.allow_state(ClientState::Character);
|
||||
client.in_game = Some(ClientIngame::Character);
|
||||
client.send_not_in_game(ServerNotInGameMsg::CharacterSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,7 +286,7 @@ impl StateExt for State {
|
||||
.join()
|
||||
{
|
||||
if uid == u || uid == t {
|
||||
client.notify(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
client.send_msg(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -294,7 +298,7 @@ impl StateExt for State {
|
||||
if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) {
|
||||
for (client, pos) in (&mut ecs.write_storage::<Client>(), &positions).join() {
|
||||
if is_within(comp::ChatMsg::SAY_DISTANCE, pos, speaker_pos) {
|
||||
client.notify(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
client.send_msg(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -306,7 +310,7 @@ impl StateExt for State {
|
||||
if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) {
|
||||
for (client, pos) in (&mut ecs.write_storage::<Client>(), &positions).join() {
|
||||
if is_within(comp::ChatMsg::REGION_DISTANCE, pos, speaker_pos) {
|
||||
client.notify(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
client.send_msg(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -318,7 +322,7 @@ impl StateExt for State {
|
||||
if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) {
|
||||
for (client, pos) in (&mut ecs.write_storage::<Client>(), &positions).join() {
|
||||
if is_within(comp::ChatMsg::NPC_DISTANCE, pos, speaker_pos) {
|
||||
client.notify(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
client.send_msg(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -332,7 +336,7 @@ impl StateExt for State {
|
||||
.join()
|
||||
{
|
||||
if s == &faction.0 {
|
||||
client.notify(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
client.send_msg(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -344,7 +348,7 @@ impl StateExt for State {
|
||||
.join()
|
||||
{
|
||||
if g == group {
|
||||
client.notify(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
client.send_msg(ServerMsg::ChatMsg(resolved_msg.clone()));
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -355,9 +359,9 @@ impl StateExt for State {
|
||||
fn notify_registered_clients(&self, msg: ServerMsg) {
|
||||
for client in (&mut self.ecs().write_storage::<Client>())
|
||||
.join()
|
||||
.filter(|c| c.is_registered())
|
||||
.filter(|c| c.registered)
|
||||
{
|
||||
client.notify(msg.clone());
|
||||
client.send_msg(msg.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,7 +388,7 @@ impl StateExt for State {
|
||||
.try_map(|e| uids.get(e).copied())
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| c.notify(ServerMsg::GroupUpdate(g)));
|
||||
.map(|(g, c)| c.send_in_game(ServerInGameMsg::GroupUpdate(g)));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Player, Pos, Vel},
|
||||
msg::ServerMsg,
|
||||
msg::{ServerInGameMsg, ServerMsg},
|
||||
outcome::Outcome,
|
||||
region::{Event as RegionEvent, RegionMap},
|
||||
span,
|
||||
@ -107,7 +107,7 @@ impl<'a> System<'a> for Sys {
|
||||
let mut subscribers = (&mut clients, &entities, &subscriptions, &positions)
|
||||
.join()
|
||||
.filter_map(|(client, entity, subscription, pos)| {
|
||||
if client.is_ingame() && subscription.regions.contains(&key) {
|
||||
if client.in_game.is_some() && subscription.regions.contains(&key) {
|
||||
Some((client, &subscription.regions, entity, *pos))
|
||||
} else {
|
||||
None
|
||||
@ -143,7 +143,7 @@ impl<'a> System<'a> for Sys {
|
||||
// Client doesn't need to know about itself
|
||||
&& *client_entity != entity
|
||||
{
|
||||
client.notify(create_msg.clone());
|
||||
client.send_msg(create_msg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -157,7 +157,7 @@ impl<'a> System<'a> for Sys {
|
||||
.map(|key| !regions.contains(key))
|
||||
.unwrap_or(true)
|
||||
{
|
||||
client.notify(ServerMsg::DeleteEntity(uid));
|
||||
client.send_msg(ServerMsg::DeleteEntity(uid));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,8 +177,8 @@ impl<'a> System<'a> for Sys {
|
||||
let entity_sync_msg = ServerMsg::EntitySync(entity_sync_package);
|
||||
let comp_sync_msg = ServerMsg::CompSync(comp_sync_package);
|
||||
subscribers.iter_mut().for_each(move |(client, _, _, _)| {
|
||||
client.notify(entity_sync_msg.clone());
|
||||
client.notify(comp_sync_msg.clone());
|
||||
client.send_msg(entity_sync_msg.clone());
|
||||
client.send_msg(comp_sync_msg.clone());
|
||||
});
|
||||
|
||||
let mut send_msg = |msg: ServerMsg,
|
||||
@ -212,7 +212,7 @@ impl<'a> System<'a> for Sys {
|
||||
true // Closer than 100 blocks
|
||||
}
|
||||
} {
|
||||
client.notify(msg.clone());
|
||||
client.send_msg(msg.clone());
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -303,7 +303,7 @@ impl<'a> System<'a> for Sys {
|
||||
(&mut clients, &subscriptions)
|
||||
.join()
|
||||
.filter_map(|(client, subscription)| {
|
||||
if client.is_ingame() && subscription.regions.contains(®ion_key) {
|
||||
if client.in_game.is_some() && subscription.regions.contains(®ion_key) {
|
||||
Some(client)
|
||||
} else {
|
||||
None
|
||||
@ -311,7 +311,7 @@ impl<'a> System<'a> for Sys {
|
||||
})
|
||||
{
|
||||
for uid in &deleted {
|
||||
client.notify(ServerMsg::DeleteEntity(Uid(*uid)));
|
||||
client.send_msg(ServerMsg::DeleteEntity(Uid(*uid)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -320,7 +320,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Sync inventories
|
||||
for (client, inventory, update) in (&mut clients, &inventories, &inventory_updates).join() {
|
||||
client.notify(ServerMsg::InventoryUpdate(
|
||||
client.send_in_game(ServerInGameMsg::InventoryUpdate(
|
||||
inventory.clone(),
|
||||
update.event(),
|
||||
));
|
||||
@ -341,7 +341,7 @@ impl<'a> System<'a> for Sys {
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if !outcomes.is_empty() {
|
||||
client.notify(ServerMsg::Outcomes(outcomes));
|
||||
client.send_in_game(ServerInGameMsg::Outcomes(outcomes));
|
||||
}
|
||||
}
|
||||
outcomes.clear();
|
||||
@ -355,7 +355,7 @@ impl<'a> System<'a> for Sys {
|
||||
// system?)
|
||||
let tof_msg = ServerMsg::TimeOfDay(*time_of_day);
|
||||
for client in (&mut clients).join() {
|
||||
client.notify(tof_msg.clone());
|
||||
client.send_msg(tof_msg.clone());
|
||||
}
|
||||
|
||||
timer.end();
|
||||
|
@ -2,7 +2,7 @@ use super::SysTimer;
|
||||
use crate::client::Client;
|
||||
use common::{
|
||||
comp::group::{Invite, PendingInvites},
|
||||
msg::{InviteAnswer, ServerMsg},
|
||||
msg::{InviteAnswer, ServerInGameMsg},
|
||||
span,
|
||||
sync::Uid,
|
||||
};
|
||||
@ -54,7 +54,7 @@ impl<'a> System<'a> for Sys {
|
||||
if let (Some(client), Some(target)) =
|
||||
(clients.get_mut(*inviter), uids.get(invitee).copied())
|
||||
{
|
||||
client.notify(ServerMsg::InviteComplete {
|
||||
client.send_in_game(ServerInGameMsg::InviteComplete {
|
||||
target,
|
||||
answer: InviteAnswer::TimedOut,
|
||||
})
|
||||
|
@ -15,9 +15,10 @@ use common::{
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{
|
||||
validate_chat_msg, CharacterInfo, ChatMsgValidationError, ClientMsg, ClientState,
|
||||
DisconnectReason, PlayerInfo, PlayerListUpdate, RequestStateError, ServerMsg,
|
||||
MAX_BYTES_CHAT_MSG,
|
||||
validate_chat_msg, CharacterInfo, ChatMsgValidationError, ClientInGameMsg, ClientIngame,
|
||||
ClientMsg, ClientNotInGameMsg, ClientRegisterMsg, DisconnectReason, PingMsg, PlayerInfo,
|
||||
PlayerListUpdate, RegisterError, ServerInGameMsg, ServerMsg, ServerNotInGameMsg,
|
||||
ServerRegisterAnswerMsg, MAX_BYTES_CHAT_MSG,
|
||||
},
|
||||
span,
|
||||
state::{BlockChange, Time},
|
||||
@ -32,13 +33,435 @@ use hashbrown::HashMap;
|
||||
use specs::{
|
||||
Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage,
|
||||
};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
impl Sys {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn handle_client_msg(
|
||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
||||
new_chat_msgs: &mut Vec<(Option<specs::Entity>, UnresolvedChatMsg)>,
|
||||
entity: specs::Entity,
|
||||
client: &mut Client,
|
||||
player_metrics: &ReadExpect<'_, PlayerMetrics>,
|
||||
uids: &ReadStorage<'_, Uid>,
|
||||
chat_modes: &ReadStorage<'_, ChatMode>,
|
||||
msg: ClientMsg,
|
||||
) -> Result<(), crate::error::Error> {
|
||||
match msg {
|
||||
ClientMsg::ChatMsg(message) => {
|
||||
if client.registered {
|
||||
match validate_chat_msg(&message) {
|
||||
Ok(()) => {
|
||||
if let Some(from) = uids.get(entity) {
|
||||
let mode = chat_modes.get(entity).cloned().unwrap_or_default();
|
||||
let msg = mode.new_message(*from, message);
|
||||
new_chat_msgs.push((Some(entity), msg));
|
||||
} else {
|
||||
error!("Could not send message. Missing player uid");
|
||||
}
|
||||
},
|
||||
Err(ChatMsgValidationError::TooLong) => {
|
||||
let max = MAX_BYTES_CHAT_MSG;
|
||||
let len = message.len();
|
||||
warn!(?len, ?max, "Received a chat message that's too long")
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientMsg::Command(message) => {
|
||||
if client.registered {
|
||||
match validate_chat_msg(&message) {
|
||||
Ok(()) => {
|
||||
if let Some(from) = uids.get(entity) {
|
||||
let mode = chat_modes.get(entity).cloned().unwrap_or_default();
|
||||
let msg = mode.new_message(*from, message);
|
||||
new_chat_msgs.push((Some(entity), msg));
|
||||
} else {
|
||||
error!("Could not send message. Missing player uid");
|
||||
}
|
||||
},
|
||||
Err(ChatMsgValidationError::TooLong) => {
|
||||
let max = MAX_BYTES_CHAT_MSG;
|
||||
let len = message.len();
|
||||
warn!(?len, ?max, "Received a chat message that's too long")
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientMsg::Disconnect => {
|
||||
client.send_msg(ServerMsg::Disconnect(DisconnectReason::Requested));
|
||||
},
|
||||
ClientMsg::Terminate => {
|
||||
debug!(?entity, "Client send message to termitate session");
|
||||
player_metrics
|
||||
.clients_disconnected
|
||||
.with_label_values(&["gracefully"])
|
||||
.inc();
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn handle_client_in_game_msg(
|
||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
||||
entity: specs::Entity,
|
||||
client: &mut Client,
|
||||
terrain: &ReadExpect<'_, TerrainGrid>,
|
||||
network_metrics: &ReadExpect<'_, NetworkRequestMetrics>,
|
||||
can_build: &ReadStorage<'_, CanBuild>,
|
||||
force_updates: &ReadStorage<'_, ForceUpdate>,
|
||||
stats: &mut WriteStorage<'_, Stats>,
|
||||
block_changes: &mut Write<'_, BlockChange>,
|
||||
positions: &mut WriteStorage<'_, Pos>,
|
||||
velocities: &mut WriteStorage<'_, Vel>,
|
||||
orientations: &mut WriteStorage<'_, Ori>,
|
||||
players: &mut WriteStorage<'_, Player>,
|
||||
controllers: &mut WriteStorage<'_, Controller>,
|
||||
settings: &Read<'_, Settings>,
|
||||
msg: ClientInGameMsg,
|
||||
) -> Result<(), crate::error::Error> {
|
||||
if !client.in_game.is_some() {
|
||||
debug!(?entity, "client is not in_game, ignoring msg");
|
||||
trace!(?msg, "ignored msg content");
|
||||
if matches!(msg, ClientInGameMsg::TerrainChunkRequest{ .. }) {
|
||||
network_metrics.chunks_request_dropped.inc();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
match msg {
|
||||
// Go back to registered state (char selection screen)
|
||||
ClientInGameMsg::ExitInGame => {
|
||||
client.in_game = None;
|
||||
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
||||
client.send_in_game(ServerInGameMsg::ExitInGameSuccess);
|
||||
},
|
||||
ClientInGameMsg::SetViewDistance(view_distance) => {
|
||||
players.get_mut(entity).map(|player| {
|
||||
player.view_distance = Some(
|
||||
settings
|
||||
.max_view_distance
|
||||
.map(|max| view_distance.min(max))
|
||||
.unwrap_or(view_distance),
|
||||
)
|
||||
});
|
||||
|
||||
if settings
|
||||
.max_view_distance
|
||||
.map(|max| view_distance > max)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
client.send_in_game(ServerInGameMsg::SetViewDistance(
|
||||
settings.max_view_distance.unwrap_or(0),
|
||||
));
|
||||
}
|
||||
},
|
||||
ClientInGameMsg::ControllerInputs(inputs) => {
|
||||
if let Some(ClientIngame::Character) = client.in_game {
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.inputs.update_with_new(inputs);
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientInGameMsg::ControlEvent(event) => {
|
||||
if let Some(ClientIngame::Character) = client.in_game {
|
||||
// Skip respawn if client entity is alive
|
||||
if let ControlEvent::Respawn = event {
|
||||
if stats.get(entity).map_or(true, |s| !s.is_dead) {
|
||||
//Todo: comment why return!
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.events.push(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientInGameMsg::ControlAction(event) => {
|
||||
if let Some(ClientIngame::Character) = client.in_game {
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.actions.push(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientInGameMsg::PlayerPhysics { pos, vel, ori } => {
|
||||
if let Some(ClientIngame::Character) = client.in_game {
|
||||
if force_updates.get(entity).is_none()
|
||||
&& stats.get(entity).map_or(true, |s| !s.is_dead)
|
||||
{
|
||||
let _ = positions.insert(entity, pos);
|
||||
let _ = velocities.insert(entity, vel);
|
||||
let _ = orientations.insert(entity, ori);
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientInGameMsg::BreakBlock(pos) => {
|
||||
if let Some(block) = can_build.get(entity).and_then(|_| terrain.get(pos).ok()) {
|
||||
block_changes.set(pos, block.into_vacant());
|
||||
}
|
||||
},
|
||||
ClientInGameMsg::PlaceBlock(pos, block) => {
|
||||
if can_build.get(entity).is_some() {
|
||||
block_changes.try_set(pos, block);
|
||||
}
|
||||
},
|
||||
ClientInGameMsg::TerrainChunkRequest { key } => {
|
||||
let in_vd = if let (Some(view_distance), Some(pos)) = (
|
||||
players.get(entity).and_then(|p| p.view_distance),
|
||||
positions.get(entity),
|
||||
) {
|
||||
pos.0.xy().map(|e| e as f64).distance(
|
||||
key.map(|e| e as f64 + 0.5) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64),
|
||||
) < (view_distance as f64 - 1.0 + 2.5 * 2.0_f64.sqrt())
|
||||
* TerrainChunkSize::RECT_SIZE.x as f64
|
||||
} else {
|
||||
true
|
||||
};
|
||||
if in_vd {
|
||||
match terrain.get_key(key) {
|
||||
Some(chunk) => {
|
||||
network_metrics.chunks_served_from_memory.inc();
|
||||
client.send_in_game(ServerInGameMsg::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Ok(Box::new(chunk.clone())),
|
||||
})
|
||||
},
|
||||
None => {
|
||||
network_metrics.chunks_generation_triggered.inc();
|
||||
server_emitter.emit(ServerEvent::ChunkRequest(entity, key))
|
||||
},
|
||||
}
|
||||
} else {
|
||||
network_metrics.chunks_request_dropped.inc();
|
||||
}
|
||||
},
|
||||
ClientInGameMsg::UnlockSkill(skill) => {
|
||||
stats
|
||||
.get_mut(entity)
|
||||
.map(|s| s.skill_set.unlock_skill(skill));
|
||||
},
|
||||
ClientInGameMsg::RefundSkill(skill) => {
|
||||
stats
|
||||
.get_mut(entity)
|
||||
.map(|s| s.skill_set.refund_skill(skill));
|
||||
},
|
||||
ClientInGameMsg::UnlockSkillGroup(skill_group_type) => {
|
||||
stats
|
||||
.get_mut(entity)
|
||||
.map(|s| s.skill_set.unlock_skill_group(skill_group_type));
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn handle_client_not_in_game_msg(
|
||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
||||
new_chat_msgs: &mut Vec<(Option<specs::Entity>, UnresolvedChatMsg)>,
|
||||
entity: specs::Entity,
|
||||
client: &mut Client,
|
||||
character_loader: &ReadExpect<'_, CharacterLoader>,
|
||||
uids: &ReadStorage<'_, Uid>,
|
||||
players: &mut WriteStorage<'_, Player>,
|
||||
editable_settings: &ReadExpect<'_, EditableSettings>,
|
||||
alias_validator: &ReadExpect<'_, AliasValidator>,
|
||||
msg: ClientNotInGameMsg,
|
||||
) -> Result<(), crate::error::Error> {
|
||||
match msg {
|
||||
// Request spectator state
|
||||
ClientNotInGameMsg::Spectate => {
|
||||
if client.registered {
|
||||
client.in_game = Some(ClientIngame::Spectator)
|
||||
} else {
|
||||
debug!("dropped Spectate msg from unregistered client");
|
||||
}
|
||||
},
|
||||
ClientNotInGameMsg::Character(character_id) => {
|
||||
if client.registered && !client.in_game.is_some() {
|
||||
// Only send login message if it wasn't already
|
||||
// sent previously
|
||||
if let Some(player) = players.get(entity) {
|
||||
// Send a request to load the character's component data from the
|
||||
// DB. Once loaded, persisted components such as stats and inventory
|
||||
// will be inserted for the entity
|
||||
character_loader.load_character_data(
|
||||
entity,
|
||||
player.uuid().to_string(),
|
||||
character_id,
|
||||
);
|
||||
|
||||
// Start inserting non-persisted/default components for the entity
|
||||
// while we load the DB data
|
||||
server_emitter.emit(ServerEvent::InitCharacterData {
|
||||
entity,
|
||||
character_id,
|
||||
});
|
||||
|
||||
// Give the player a welcome message
|
||||
if !editable_settings.server_description.is_empty() {
|
||||
client.send_msg(
|
||||
ChatType::CommandInfo.server_msg(String::from(
|
||||
&*editable_settings.server_description,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
// Only send login message if it wasn't already
|
||||
// sent previously
|
||||
if !client.login_msg_sent {
|
||||
if let Some(player_uid) = uids.get(entity) {
|
||||
new_chat_msgs.push((None, UnresolvedChatMsg {
|
||||
chat_type: ChatType::Online(*player_uid),
|
||||
message: "".to_string(),
|
||||
}));
|
||||
|
||||
client.login_msg_sent = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client.send_not_in_game(ServerNotInGameMsg::CharacterDataLoadError(
|
||||
String::from("Failed to fetch player entity"),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
let registered = client.registered;
|
||||
let in_game = client.in_game;
|
||||
debug!(?registered, ?in_game, "dropped Character msg from client");
|
||||
}
|
||||
},
|
||||
ClientNotInGameMsg::RequestCharacterList => {
|
||||
if let Some(player) = players.get(entity) {
|
||||
character_loader.load_character_list(entity, player.uuid().to_string())
|
||||
}
|
||||
},
|
||||
ClientNotInGameMsg::CreateCharacter { alias, tool, body } => {
|
||||
if let Err(error) = alias_validator.validate(&alias) {
|
||||
debug!(?error, ?alias, "denied alias as it contained a banned word");
|
||||
client.send_not_in_game(ServerNotInGameMsg::CharacterActionError(
|
||||
error.to_string(),
|
||||
));
|
||||
} else if let Some(player) = players.get(entity) {
|
||||
character_creator::create_character(
|
||||
entity,
|
||||
player.uuid().to_string(),
|
||||
alias,
|
||||
tool,
|
||||
body,
|
||||
character_loader,
|
||||
);
|
||||
}
|
||||
},
|
||||
ClientNotInGameMsg::DeleteCharacter(character_id) => {
|
||||
if let Some(player) = players.get(entity) {
|
||||
character_loader.delete_character(
|
||||
entity,
|
||||
player.uuid().to_string(),
|
||||
character_id,
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn handle_ping_msg(client: &mut Client, msg: PingMsg) -> Result<(), crate::error::Error> {
|
||||
match msg {
|
||||
PingMsg::Ping => client.send_ping(PingMsg::Pong),
|
||||
PingMsg::Pong => {},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn handle_register_msg(
|
||||
player_list: &HashMap<Uid, PlayerInfo>,
|
||||
new_players: &mut Vec<specs::Entity>,
|
||||
entity: specs::Entity,
|
||||
client: &mut Client,
|
||||
player_metrics: &ReadExpect<'_, PlayerMetrics>,
|
||||
login_provider: &mut WriteExpect<'_, LoginProvider>,
|
||||
admins: &mut WriteStorage<'_, Admin>,
|
||||
players: &mut WriteStorage<'_, Player>,
|
||||
settings: &Read<'_, Settings>,
|
||||
editable_settings: &ReadExpect<'_, EditableSettings>,
|
||||
msg: ClientRegisterMsg,
|
||||
) -> 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
|
||||
.register_stream
|
||||
.send(ServerRegisterAnswerMsg::Err(err))?;
|
||||
return Ok(());
|
||||
},
|
||||
Ok((username, uuid)) => (username, uuid),
|
||||
};
|
||||
|
||||
let vd = msg
|
||||
.view_distance
|
||||
.map(|vd| vd.min(settings.max_view_distance.unwrap_or(vd)));
|
||||
let player = Player::new(username.clone(), None, vd, uuid);
|
||||
let is_admin = editable_settings.admins.contains(&uuid);
|
||||
|
||||
if !player.is_valid() {
|
||||
// Invalid player
|
||||
client.register_stream.send(ServerRegisterAnswerMsg::Err(
|
||||
RegisterError::InvalidCharacter,
|
||||
))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !client.registered && client.in_game.is_none() {
|
||||
// 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.registered = true;
|
||||
client
|
||||
.register_stream
|
||||
.send(ServerRegisterAnswerMsg::Ok(()))?;
|
||||
|
||||
// Send initial player list
|
||||
client.send_msg(ServerMsg::PlayerListUpdate(PlayerListUpdate::Init(
|
||||
player_list.clone(),
|
||||
)));
|
||||
|
||||
// Add to list to notify all clients of the new player
|
||||
new_players.push(entity);
|
||||
}
|
||||
|
||||
// Limit view distance if it's too high
|
||||
// This comes after state registration so that the client actually hears it
|
||||
if settings
|
||||
.max_view_distance
|
||||
.zip(msg.view_distance)
|
||||
.map(|(max, vd)| vd > max)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
client.send_in_game(ServerInGameMsg::SetViewDistance(
|
||||
settings.max_view_distance.unwrap_or(0),
|
||||
));
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///We needed to move this to a async fn, if we would use a async closures
|
||||
/// the compiler generates to much recursion and fails to compile this
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn handle_client_msg(
|
||||
async fn handle_messages(
|
||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
||||
new_chat_msgs: &mut Vec<(Option<specs::Entity>, UnresolvedChatMsg)>,
|
||||
player_list: &HashMap<Uid, PlayerInfo>,
|
||||
@ -64,357 +487,87 @@ impl Sys {
|
||||
players: &mut WriteStorage<'_, Player>,
|
||||
controllers: &mut WriteStorage<'_, Controller>,
|
||||
settings: &Read<'_, Settings>,
|
||||
editable_settings: &ReadExpect<'_, EditableSettings>,
|
||||
alias_validator: &ReadExpect<'_, AliasValidator>,
|
||||
editable_settings: &EditableSettings,
|
||||
) -> Result<(), crate::error::Error> {
|
||||
loop {
|
||||
let msg = client.recv().await?;
|
||||
let q1 = Client::internal_recv(&client.network_error, &mut client.singleton_stream);
|
||||
let q2 = Client::internal_recv(&client.network_error, &mut client.in_game_stream);
|
||||
let q3 = Client::internal_recv(&client.network_error, &mut client.not_in_game_stream);
|
||||
let q4 = Client::internal_recv(&client.network_error, &mut client.ping_stream);
|
||||
let q5 = Client::internal_recv(&client.network_error, &mut client.register_stream);
|
||||
|
||||
let (m1, m2, m3, m4, m5) = select!(
|
||||
msg = q1.fuse() => (Some(msg?), None, None, None, None),
|
||||
msg = q2.fuse() => (None, Some(msg?), None, None, None),
|
||||
msg = q3.fuse() => (None, None, Some(msg?), None, None),
|
||||
msg = q4.fuse() => (None, None, None, Some(msg?), None),
|
||||
msg = q5.fuse() => (None, None, None, None,Some(msg?)),
|
||||
);
|
||||
*cnt += 1;
|
||||
match msg {
|
||||
// Go back to registered state (char selection screen)
|
||||
ClientMsg::ExitIngame => match client.client_state {
|
||||
// Use ClientMsg::Register instead.
|
||||
ClientState::Connected => client.error_state(RequestStateError::WrongMessage),
|
||||
ClientState::Registered => client.error_state(RequestStateError::Already),
|
||||
ClientState::Spectator | ClientState::Character => {
|
||||
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
// Request spectator state
|
||||
ClientMsg::Spectate => match client.client_state {
|
||||
// Become Registered first.
|
||||
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
||||
ClientState::Spectator => client.error_state(RequestStateError::Already),
|
||||
ClientState::Registered | ClientState::Character => {
|
||||
client.allow_state(ClientState::Spectator)
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
// Request registered state (login)
|
||||
ClientMsg::Register {
|
||||
view_distance,
|
||||
token_or_username,
|
||||
} => {
|
||||
let (username, uuid) = match login_provider.try_login(
|
||||
&token_or_username,
|
||||
&*editable_settings.admins,
|
||||
&*editable_settings.whitelist,
|
||||
&*editable_settings.banlist,
|
||||
) {
|
||||
Err(err) => {
|
||||
client.error_state(RequestStateError::RegisterDenied(err));
|
||||
break Ok(());
|
||||
},
|
||||
Ok((username, uuid)) => (username, uuid),
|
||||
};
|
||||
|
||||
let vd =
|
||||
view_distance.map(|vd| vd.min(settings.max_view_distance.unwrap_or(vd)));
|
||||
let is_admin = editable_settings.admins.contains(&uuid);
|
||||
let player = Player::new(username.clone(), None, vd, uuid);
|
||||
|
||||
if !player.is_valid() {
|
||||
// Invalid player
|
||||
client.error_state(RequestStateError::Impossible);
|
||||
break Ok(());
|
||||
}
|
||||
|
||||
match client.client_state {
|
||||
ClientState::Connected => {
|
||||
// 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.allow_state(ClientState::Registered);
|
||||
|
||||
// Send initial player list
|
||||
client.notify(ServerMsg::PlayerListUpdate(PlayerListUpdate::Init(
|
||||
player_list.clone(),
|
||||
)));
|
||||
|
||||
// Add to list to notify all clients of the new player
|
||||
new_players.push(entity);
|
||||
},
|
||||
// Use RequestState instead (No need to send `player` again).
|
||||
_ => client.error_state(RequestStateError::Impossible),
|
||||
}
|
||||
//client.allow_state(ClientState::Registered);
|
||||
|
||||
// Limit view distance if it's too high
|
||||
// This comes after state registration so that the client actually hears it
|
||||
if settings
|
||||
.max_view_distance
|
||||
.zip(view_distance)
|
||||
.map(|(max, vd)| vd > max)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
client.notify(ServerMsg::SetViewDistance(
|
||||
settings.max_view_distance.unwrap_or(0),
|
||||
));
|
||||
};
|
||||
},
|
||||
ClientMsg::SetViewDistance(view_distance) => {
|
||||
if let ClientState::Character { .. } = client.client_state {
|
||||
players.get_mut(entity).map(|player| {
|
||||
player.view_distance = Some(
|
||||
settings
|
||||
.max_view_distance
|
||||
.map(|max| view_distance.min(max))
|
||||
.unwrap_or(view_distance),
|
||||
)
|
||||
});
|
||||
|
||||
if settings
|
||||
.max_view_distance
|
||||
.map(|max| view_distance > max)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
client.notify(ServerMsg::SetViewDistance(
|
||||
settings.max_view_distance.unwrap_or(0),
|
||||
));
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientMsg::Character(character_id) => match client.client_state {
|
||||
// Become Registered first.
|
||||
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
||||
ClientState::Registered | ClientState::Spectator => {
|
||||
// Only send login message if it wasn't already
|
||||
// sent previously
|
||||
if let Some(player) = players.get(entity) {
|
||||
// Send a request to load the character's component data from the
|
||||
// DB. Once loaded, persisted components such as stats and inventory
|
||||
// will be inserted for the entity
|
||||
character_loader.load_character_data(
|
||||
entity,
|
||||
player.uuid().to_string(),
|
||||
character_id,
|
||||
);
|
||||
|
||||
// Start inserting non-persisted/default components for the entity
|
||||
// while we load the DB data
|
||||
server_emitter.emit(ServerEvent::InitCharacterData {
|
||||
entity,
|
||||
character_id,
|
||||
});
|
||||
|
||||
// Give the player a welcome message
|
||||
if !editable_settings.server_description.is_empty() {
|
||||
client.notify(ChatType::CommandInfo.server_msg(String::from(
|
||||
&*editable_settings.server_description,
|
||||
)));
|
||||
}
|
||||
|
||||
// Only send login message if it wasn't already
|
||||
// sent previously
|
||||
if !client.login_msg_sent {
|
||||
if let Some(player_uid) = uids.get(entity) {
|
||||
new_chat_msgs.push((None, UnresolvedChatMsg {
|
||||
chat_type: ChatType::Online(*player_uid),
|
||||
message: "".to_string(),
|
||||
}));
|
||||
|
||||
client.login_msg_sent = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client.notify(ServerMsg::CharacterDataLoadError(String::from(
|
||||
"Failed to fetch player entity",
|
||||
)))
|
||||
}
|
||||
},
|
||||
ClientState::Character => client.error_state(RequestStateError::Already),
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
ClientMsg::ControllerInputs(inputs) => match client.client_state {
|
||||
ClientState::Connected | ClientState::Registered | ClientState::Spectator => {
|
||||
client.error_state(RequestStateError::Impossible)
|
||||
},
|
||||
ClientState::Character => {
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.inputs.update_with_new(inputs);
|
||||
}
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
ClientMsg::ControlEvent(event) => match client.client_state {
|
||||
ClientState::Connected | ClientState::Registered | ClientState::Spectator => {
|
||||
client.error_state(RequestStateError::Impossible)
|
||||
},
|
||||
ClientState::Character => {
|
||||
// Skip respawn if client entity is alive
|
||||
if let ControlEvent::Respawn = event {
|
||||
if stats.get(entity).map_or(true, |s| !s.is_dead) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.events.push(event);
|
||||
}
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
ClientMsg::ControlAction(event) => match client.client_state {
|
||||
ClientState::Connected | ClientState::Registered | ClientState::Spectator => {
|
||||
client.error_state(RequestStateError::Impossible)
|
||||
},
|
||||
ClientState::Character => {
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.actions.push(event);
|
||||
}
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
ClientMsg::ChatMsg(message) => match client.client_state {
|
||||
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
||||
ClientState::Registered | ClientState::Spectator | ClientState::Character => {
|
||||
match validate_chat_msg(&message) {
|
||||
Ok(()) => {
|
||||
if let Some(from) = uids.get(entity) {
|
||||
let mode = chat_modes.get(entity).cloned().unwrap_or_default();
|
||||
let msg = mode.new_message(*from, message);
|
||||
new_chat_msgs.push((Some(entity), msg));
|
||||
} else {
|
||||
error!("Could not send message. Missing player uid");
|
||||
}
|
||||
},
|
||||
Err(ChatMsgValidationError::TooLong) => {
|
||||
let max = MAX_BYTES_CHAT_MSG;
|
||||
let len = message.len();
|
||||
warn!(?len, ?max, "Received a chat message that's too long")
|
||||
},
|
||||
}
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
ClientMsg::PlayerPhysics { pos, vel, ori } => match client.client_state {
|
||||
ClientState::Character => {
|
||||
if force_updates.get(entity).is_none()
|
||||
&& stats.get(entity).map_or(true, |s| !s.is_dead)
|
||||
{
|
||||
let _ = positions.insert(entity, pos);
|
||||
let _ = velocities.insert(entity, vel);
|
||||
let _ = orientations.insert(entity, ori);
|
||||
}
|
||||
},
|
||||
// Only characters can send positions.
|
||||
_ => client.error_state(RequestStateError::Impossible),
|
||||
},
|
||||
ClientMsg::BreakBlock(pos) => {
|
||||
if let Some(block) = can_build.get(entity).and_then(|_| terrain.get(pos).ok()) {
|
||||
block_changes.set(pos, block.into_vacant());
|
||||
}
|
||||
},
|
||||
ClientMsg::PlaceBlock(pos, block) => {
|
||||
if can_build.get(entity).is_some() {
|
||||
block_changes.try_set(pos, block);
|
||||
}
|
||||
},
|
||||
ClientMsg::TerrainChunkRequest { key } => match client.client_state {
|
||||
ClientState::Connected | ClientState::Registered => {
|
||||
network_metrics.chunks_request_dropped.inc();
|
||||
client.error_state(RequestStateError::Impossible);
|
||||
},
|
||||
ClientState::Spectator | ClientState::Character => {
|
||||
let in_vd = if let (Some(view_distance), Some(pos)) = (
|
||||
players.get(entity).and_then(|p| p.view_distance),
|
||||
positions.get(entity),
|
||||
) {
|
||||
pos.0.xy().map(|e| e as f64).distance(
|
||||
key.map(|e| e as f64 + 0.5)
|
||||
* TerrainChunkSize::RECT_SIZE.map(|e| e as f64),
|
||||
) < (view_distance as f64 - 1.0 + 2.5 * 2.0_f64.sqrt())
|
||||
* TerrainChunkSize::RECT_SIZE.x as f64
|
||||
} else {
|
||||
true
|
||||
};
|
||||
if in_vd {
|
||||
match terrain.get_key(key) {
|
||||
Some(chunk) => {
|
||||
network_metrics.chunks_served_from_memory.inc();
|
||||
client.notify(ServerMsg::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Ok(Box::new(chunk.clone())),
|
||||
})
|
||||
},
|
||||
None => {
|
||||
network_metrics.chunks_generation_triggered.inc();
|
||||
server_emitter.emit(ServerEvent::ChunkRequest(entity, key))
|
||||
},
|
||||
}
|
||||
} else {
|
||||
network_metrics.chunks_request_dropped.inc();
|
||||
}
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
// Always possible.
|
||||
ClientMsg::Ping => client.notify(ServerMsg::Pong),
|
||||
ClientMsg::Pong => {},
|
||||
ClientMsg::Disconnect => {
|
||||
client.notify(ServerMsg::Disconnect(DisconnectReason::Requested));
|
||||
},
|
||||
ClientMsg::Terminate => {
|
||||
debug!(?entity, "Client send message to termitate session");
|
||||
player_metrics
|
||||
.clients_disconnected
|
||||
.with_label_values(&["gracefully"])
|
||||
.inc();
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
||||
break Ok(());
|
||||
},
|
||||
ClientMsg::RequestCharacterList => {
|
||||
if let Some(player) = players.get(entity) {
|
||||
character_loader.load_character_list(entity, player.uuid().to_string())
|
||||
}
|
||||
},
|
||||
ClientMsg::CreateCharacter { alias, tool, body } => {
|
||||
if let Err(error) = alias_validator.validate(&alias) {
|
||||
debug!(?error, ?alias, "denied alias as it contained a banned word");
|
||||
client.notify(ServerMsg::CharacterActionError(error.to_string()));
|
||||
} else if let Some(player) = players.get(entity) {
|
||||
character_creator::create_character(
|
||||
entity,
|
||||
player.uuid().to_string(),
|
||||
alias,
|
||||
tool,
|
||||
body,
|
||||
character_loader,
|
||||
);
|
||||
}
|
||||
},
|
||||
ClientMsg::DeleteCharacter(character_id) => {
|
||||
if let Some(player) = players.get(entity) {
|
||||
character_loader.delete_character(
|
||||
entity,
|
||||
player.uuid().to_string(),
|
||||
character_id,
|
||||
);
|
||||
}
|
||||
},
|
||||
ClientMsg::UnlockSkill(skill) => {
|
||||
stats
|
||||
.get_mut(entity)
|
||||
.map(|s| s.skill_set.unlock_skill(skill));
|
||||
},
|
||||
ClientMsg::RefundSkill(skill) => {
|
||||
stats
|
||||
.get_mut(entity)
|
||||
.map(|s| s.skill_set.refund_skill(skill));
|
||||
},
|
||||
ClientMsg::UnlockSkillGroup(skill_group_type) => {
|
||||
stats
|
||||
.get_mut(entity)
|
||||
.map(|s| s.skill_set.unlock_skill_group(skill_group_type));
|
||||
},
|
||||
if let Some(msg) = m1 {
|
||||
Self::handle_client_msg(
|
||||
server_emitter,
|
||||
new_chat_msgs,
|
||||
entity,
|
||||
client,
|
||||
player_metrics,
|
||||
uids,
|
||||
chat_modes,
|
||||
msg,
|
||||
)?;
|
||||
}
|
||||
if let Some(msg) = m2 {
|
||||
Self::handle_client_in_game_msg(
|
||||
server_emitter,
|
||||
entity,
|
||||
client,
|
||||
terrain,
|
||||
network_metrics,
|
||||
can_build,
|
||||
force_updates,
|
||||
stats,
|
||||
block_changes,
|
||||
positions,
|
||||
velocities,
|
||||
orientations,
|
||||
players,
|
||||
controllers,
|
||||
settings,
|
||||
msg,
|
||||
)?;
|
||||
}
|
||||
if let Some(msg) = m3 {
|
||||
Self::handle_client_not_in_game_msg(
|
||||
server_emitter,
|
||||
new_chat_msgs,
|
||||
entity,
|
||||
client,
|
||||
character_loader,
|
||||
uids,
|
||||
players,
|
||||
editable_settings,
|
||||
alias_validator,
|
||||
msg,
|
||||
)?;
|
||||
}
|
||||
if let Some(msg) = m4 {
|
||||
Self::handle_ping_msg(client, msg)?;
|
||||
}
|
||||
if let Some(msg) = m5 {
|
||||
Self::handle_register_msg(
|
||||
player_list,
|
||||
new_players,
|
||||
entity,
|
||||
client,
|
||||
player_metrics,
|
||||
login_provider,
|
||||
admins,
|
||||
players,
|
||||
settings,
|
||||
editable_settings,
|
||||
msg,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -448,8 +601,8 @@ impl<'a> System<'a> for Sys {
|
||||
WriteStorage<'a, Client>,
|
||||
WriteStorage<'a, Controller>,
|
||||
Read<'a, Settings>,
|
||||
ReadExpect<'a, AliasValidator>,
|
||||
ReadExpect<'a, EditableSettings>,
|
||||
ReadExpect<'a, AliasValidator>,
|
||||
);
|
||||
|
||||
#[allow(clippy::match_ref_pats)] // TODO: Pending review in #587
|
||||
@ -481,8 +634,8 @@ impl<'a> System<'a> for Sys {
|
||||
mut clients,
|
||||
mut controllers,
|
||||
settings,
|
||||
alias_validator,
|
||||
editable_settings,
|
||||
alias_validator,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
span!(_guard, "run", "message::Sys::run");
|
||||
@ -515,7 +668,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let network_err: Result<(), crate::error::Error> = block_on(async {
|
||||
//TIMEOUT 0.02 ms for msg handling
|
||||
let work_future = Self::handle_client_msg(
|
||||
let work_future = Self::handle_messages(
|
||||
&mut server_emitter,
|
||||
&mut new_chat_msgs,
|
||||
&player_list,
|
||||
@ -541,8 +694,8 @@ impl<'a> System<'a> for Sys {
|
||||
&mut players,
|
||||
&mut controllers,
|
||||
&settings,
|
||||
&alias_validator,
|
||||
&editable_settings,
|
||||
&alias_validator,
|
||||
);
|
||||
select!(
|
||||
_ = Delay::new(std::time::Duration::from_micros(20)).fuse() => Ok(()),
|
||||
@ -573,7 +726,7 @@ impl<'a> System<'a> for Sys {
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
||||
} else if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 * 0.5 {
|
||||
// Try pinging the client if the timeout is nearing.
|
||||
client.notify(ServerMsg::Ping);
|
||||
client.send_ping(PingMsg::Ping);
|
||||
}
|
||||
}
|
||||
|
||||
@ -587,8 +740,8 @@ impl<'a> System<'a> for Sys {
|
||||
is_admin: admins.get(entity).is_some(),
|
||||
character: None, // new players will be on character select.
|
||||
}));
|
||||
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
||||
client.notify(msg.clone())
|
||||
for client in (&mut clients).join().filter(|c| c.registered) {
|
||||
client.send_msg(msg.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ impl<'a> System<'a> for Sys {
|
||||
)
|
||||
.join()
|
||||
.filter_map(|(client, s, pos, player, e)| {
|
||||
if client.is_ingame() {
|
||||
if client.in_game.is_some() {
|
||||
player.view_distance.map(|v| (client, s, pos, v, e))
|
||||
} else {
|
||||
None
|
||||
@ -153,7 +153,7 @@ impl<'a> System<'a> for Sys {
|
||||
.map(|key| subscription.regions.contains(key))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
client.notify(ServerMsg::DeleteEntity(uid));
|
||||
client.send_msg(ServerMsg::DeleteEntity(uid));
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -161,7 +161,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
// Tell client to delete entities in the region
|
||||
for (&uid, _) in (&uids, region.entities()).join() {
|
||||
client.notify(ServerMsg::DeleteEntity(uid));
|
||||
client.send_msg(ServerMsg::DeleteEntity(uid));
|
||||
}
|
||||
}
|
||||
// Send deleted entities since they won't be processed for this client in entity
|
||||
@ -171,7 +171,7 @@ impl<'a> System<'a> for Sys {
|
||||
.iter()
|
||||
.flat_map(|v| v.iter())
|
||||
{
|
||||
client.notify(ServerMsg::DeleteEntity(Uid(*uid)));
|
||||
client.send_msg(ServerMsg::DeleteEntity(Uid(*uid)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ impl<'a> System<'a> for Sys {
|
||||
{
|
||||
// Send message to create entity and tracked components and physics
|
||||
// components
|
||||
client.notify(ServerMsg::CreateEntity(
|
||||
client.send_msg(ServerMsg::CreateEntity(
|
||||
tracked_comps.create_entity_package(
|
||||
entity,
|
||||
Some(*pos),
|
||||
@ -249,7 +249,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) {
|
||||
.join()
|
||||
{
|
||||
// Send message to create entity and tracked components and physics components
|
||||
client.notify(ServerMsg::CreateEntity(
|
||||
client.send_msg(ServerMsg::CreateEntity(
|
||||
tracked_comps.create_entity_package(
|
||||
entity,
|
||||
Some(*pos),
|
||||
|
@ -4,7 +4,7 @@ use common::{
|
||||
comp::{self, bird_medium, Alignment, Player, Pos},
|
||||
event::{EventBus, ServerEvent},
|
||||
generation::get_npc_name,
|
||||
msg::ServerMsg,
|
||||
msg::ServerInGameMsg,
|
||||
npc::NPC_NAMES,
|
||||
span,
|
||||
state::TerrainChanges,
|
||||
@ -63,7 +63,7 @@ impl<'a> System<'a> for Sys {
|
||||
Ok((chunk, supplement)) => (chunk, supplement),
|
||||
Err(Some(entity)) => {
|
||||
if let Some(client) = clients.get_mut(entity) {
|
||||
client.notify(ServerMsg::TerrainChunkUpdate {
|
||||
client.send_in_game(ServerInGameMsg::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Err(()),
|
||||
});
|
||||
@ -90,7 +90,7 @@ impl<'a> System<'a> for Sys {
|
||||
.magnitude_squared();
|
||||
|
||||
if adjusted_dist_sqr <= view_distance.pow(2) {
|
||||
client.notify(ServerMsg::TerrainChunkUpdate {
|
||||
client.send_in_game(ServerInGameMsg::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Ok(Box::new(chunk.clone())),
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ use super::SysTimer;
|
||||
use crate::client::Client;
|
||||
use common::{
|
||||
comp::{Player, Pos},
|
||||
msg::ServerMsg,
|
||||
msg::ServerInGameMsg,
|
||||
span,
|
||||
state::TerrainChanges,
|
||||
terrain::TerrainGrid,
|
||||
@ -38,7 +38,7 @@ impl<'a> System<'a> for Sys {
|
||||
.map(|vd| super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, vd))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
client.notify(ServerMsg::TerrainChunkUpdate {
|
||||
client.send_in_game(ServerInGameMsg::TerrainChunkUpdate {
|
||||
key: *chunk_key,
|
||||
chunk: Ok(Box::new(match terrain.get_key(*chunk_key) {
|
||||
Some(chunk) => chunk.clone(),
|
||||
@ -51,10 +51,10 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// TODO: Don't send all changed blocks to all clients
|
||||
// Sync changed blocks
|
||||
let msg = ServerMsg::TerrainBlockUpdates(terrain_changes.modified_blocks.clone());
|
||||
let msg = ServerInGameMsg::TerrainBlockUpdates(terrain_changes.modified_blocks.clone());
|
||||
for (player, client) in (&players, &mut clients).join() {
|
||||
if player.view_distance.is_some() {
|
||||
client.notify(msg.clone());
|
||||
client.send_in_game(msg.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ impl<'a> System<'a> for Sys {
|
||||
if let Ok(wp_old) = waypoints.insert(entity, Waypoint::new(player_pos.0, *time))
|
||||
{
|
||||
if wp_old.map_or(true, |w| w.elapsed(*time) > NOTIFY_TIME) {
|
||||
client.notify(ServerMsg::Notification(Notification::WaypointSaved));
|
||||
client.send_msg(ServerMsg::Notification(Notification::WaypointSaved));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
Direction, GlobalState, PlayState, PlayStateResult,
|
||||
};
|
||||
use client::{self, Client};
|
||||
use common::{assets::Asset, comp, msg::ClientState, span, state::DeltaTime};
|
||||
use common::{assets::Asset, comp, span, state::DeltaTime};
|
||||
use specs::WorldExt;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use tracing::error;
|
||||
@ -61,8 +61,8 @@ impl PlayState for CharSelectionState {
|
||||
|
||||
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<WinEvent>) -> PlayStateResult {
|
||||
span!(_guard, "tick", "<CharSelectionState as PlayState>::tick");
|
||||
let client_state = self.client.borrow().get_client_state();
|
||||
if let ClientState::Pending | ClientState::Registered = client_state {
|
||||
let client_in_game = self.client.borrow().get_in_game();
|
||||
if client_in_game.is_none() {
|
||||
// Handle window events
|
||||
for event in events {
|
||||
if self.char_selection_ui.handle_event(event.clone()) {
|
||||
|
@ -20,7 +20,6 @@ use common::{
|
||||
MAX_PICKUP_RANGE_SQR,
|
||||
},
|
||||
event::EventBus,
|
||||
msg::ClientState,
|
||||
outcome::Outcome,
|
||||
span,
|
||||
terrain::{Block, BlockKind},
|
||||
@ -213,8 +212,11 @@ impl PlayState for SessionState {
|
||||
|
||||
// TODO: can this be a method on the session or are there borrowcheck issues?
|
||||
|
||||
let client_state = self.client.borrow().get_client_state();
|
||||
if let ClientState::Pending | ClientState::Character = client_state {
|
||||
let (client_in_game, client_registered) = {
|
||||
let client = self.client.borrow();
|
||||
(client.get_in_game(), client.get_registered())
|
||||
};
|
||||
if client_in_game.is_none() {
|
||||
// Update MyEntity
|
||||
// Note: Alternatively, the client could emit an event when the entity changes
|
||||
// which may or may not be more elegant
|
||||
@ -1088,7 +1090,7 @@ impl PlayState for SessionState {
|
||||
self.cleanup();
|
||||
|
||||
PlayStateResult::Continue
|
||||
} else if let ClientState::Registered = client_state {
|
||||
} else if client_registered && client_in_game.is_none() {
|
||||
PlayStateResult::Switch(Box::new(CharSelectionState::new(
|
||||
global_state,
|
||||
Rc::clone(&self.client),
|
||||
|
Loading…
Reference in New Issue
Block a user