mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
extract a Presence Component, which is server
only and has state of Player
and Client
. Presence is only valid for Clients that are in game
This commit is contained in:
parent
084b60d7ec
commit
00456c8373
@ -25,9 +25,9 @@ use common::{
|
||||
},
|
||||
event::{EventBus, LocalEvent},
|
||||
msg::{
|
||||
validate_chat_msg, ChatMsgValidationError, ClientGeneral, ClientInGame, ClientMsg,
|
||||
ClientRegister, ClientType, DisconnectReason, InviteAnswer, Notification, PingMsg,
|
||||
PlayerInfo, PlayerListUpdate, RegisterError, ServerGeneral, ServerInfo, ServerInit,
|
||||
validate_chat_msg, ChatMsgValidationError, ClientGeneral, ClientMsg, ClientRegister,
|
||||
ClientType, DisconnectReason, InviteAnswer, Notification, PingMsg, PlayerInfo,
|
||||
PlayerListUpdate, PresenceKind, RegisterError, ServerGeneral, ServerInfo, ServerInit,
|
||||
ServerRegisterAnswer, MAX_BYTES_CHAT_MSG,
|
||||
},
|
||||
outcome::Outcome,
|
||||
@ -71,7 +71,7 @@ pub enum Event {
|
||||
|
||||
pub struct Client {
|
||||
registered: bool,
|
||||
in_game: Option<ClientInGame>,
|
||||
presence: Option<PresenceKind>,
|
||||
thread_pool: ThreadPool,
|
||||
pub server_info: ServerInfo,
|
||||
/// Just the "base" layer for LOD; currently includes colors and nothing
|
||||
@ -98,7 +98,6 @@ pub struct Client {
|
||||
pub world_map: (Arc<DynamicImage>, Vec2<u16>, Vec2<f32>),
|
||||
pub player_list: HashMap<Uid, PlayerInfo>,
|
||||
pub character_list: CharacterList,
|
||||
pub active_character_id: Option<CharacterId>,
|
||||
recipe_book: RecipeBook,
|
||||
available_recipes: HashSet<String>,
|
||||
|
||||
@ -376,7 +375,7 @@ impl Client {
|
||||
|
||||
Ok(Self {
|
||||
registered: false,
|
||||
in_game: None,
|
||||
presence: None,
|
||||
thread_pool,
|
||||
server_info,
|
||||
world_map,
|
||||
@ -385,7 +384,6 @@ impl Client {
|
||||
lod_horizon,
|
||||
player_list: HashMap::new(),
|
||||
character_list: CharacterList::default(),
|
||||
active_character_id: None,
|
||||
recipe_book,
|
||||
available_recipes: HashSet::default(),
|
||||
|
||||
@ -467,12 +465,12 @@ impl Client {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
const C_TYPE: ClientType = ClientType::Game;
|
||||
let verified = msg.verify(C_TYPE, self.registered, self.in_game);
|
||||
let verified = msg.verify(C_TYPE, self.registered, self.presence);
|
||||
assert!(
|
||||
verified,
|
||||
format!(
|
||||
"c_type: {:?}, registered: {}, in_game: {:?}, msg: {:?}",
|
||||
C_TYPE, self.registered, self.in_game, msg
|
||||
"c_type: {:?}, registered: {}, presence: {:?}, msg: {:?}",
|
||||
C_TYPE, self.registered, self.presence, msg
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -528,9 +526,7 @@ impl Client {
|
||||
self.send_msg(ClientGeneral::Character(character_id));
|
||||
|
||||
//Assume we are in_game unless server tells us otherwise
|
||||
self.in_game = Some(ClientInGame::Character);
|
||||
|
||||
self.active_character_id = Some(character_id);
|
||||
self.presence = Some(PresenceKind::Character(character_id));
|
||||
}
|
||||
|
||||
/// Load the current players character list
|
||||
@ -556,7 +552,7 @@ impl Client {
|
||||
debug!("Sending logout from server");
|
||||
self.send_msg(ClientGeneral::Terminate);
|
||||
self.registered = false;
|
||||
self.in_game = None;
|
||||
self.presence = None;
|
||||
}
|
||||
|
||||
/// Request a state transition to `ClientState::Registered` from an ingame
|
||||
@ -922,7 +918,7 @@ impl Client {
|
||||
|
||||
// 1) Handle input from frontend.
|
||||
// Pass character actions from frontend input to the player's entity.
|
||||
if self.in_game.is_some() {
|
||||
if self.presence.is_some() {
|
||||
if let Err(e) = self
|
||||
.state
|
||||
.ecs()
|
||||
@ -1093,7 +1089,7 @@ impl Client {
|
||||
}
|
||||
|
||||
// 6) Update the server about the player's physics attributes.
|
||||
if self.in_game.is_some() {
|
||||
if self.presence.is_some() {
|
||||
if let (Some(pos), Some(vel), Some(ori)) = (
|
||||
self.state.read_storage().get(self.entity).cloned(),
|
||||
self.state.read_storage().get(self.entity).cloned(),
|
||||
@ -1375,9 +1371,9 @@ impl Client {
|
||||
};
|
||||
frontend_events.push(Event::Chat(comp::ChatType::Meta.chat_msg(msg)));
|
||||
},
|
||||
// Cleanup for when the client goes back to the `in_game = None`
|
||||
// Cleanup for when the client goes back to the `presence = None`
|
||||
ServerGeneral::ExitInGameSuccess => {
|
||||
self.in_game = None;
|
||||
self.presence = None;
|
||||
self.clean_state();
|
||||
},
|
||||
ServerGeneral::InventoryUpdate(mut inventory, event) => {
|
||||
@ -1438,7 +1434,7 @@ impl Client {
|
||||
},
|
||||
ServerGeneral::CharacterDataLoadError(error) => {
|
||||
trace!("Handling join error by server");
|
||||
self.in_game = None;
|
||||
self.presence = None;
|
||||
self.clean_state();
|
||||
self.character_list.error = Some(error);
|
||||
},
|
||||
@ -1548,7 +1544,7 @@ impl Client {
|
||||
|
||||
pub fn uid(&self) -> Option<Uid> { self.state.read_component_copied(self.entity) }
|
||||
|
||||
pub fn in_game(&self) -> Option<ClientInGame> { self.in_game }
|
||||
pub fn presence(&self) -> Option<PresenceKind> { self.presence }
|
||||
|
||||
pub fn registered(&self) -> bool { self.registered }
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::character::CharacterId;
|
||||
use authc::Uuid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::{Component, FlaggedStorage, NullStorage};
|
||||
@ -9,25 +8,11 @@ const MAX_ALIAS_LEN: usize = 32;
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Player {
|
||||
pub alias: String,
|
||||
pub character_id: Option<CharacterId>,
|
||||
pub view_distance: Option<u32>,
|
||||
uuid: Uuid,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new(
|
||||
alias: String,
|
||||
character_id: Option<CharacterId>,
|
||||
view_distance: Option<u32>,
|
||||
uuid: Uuid,
|
||||
) -> Self {
|
||||
Self {
|
||||
alias,
|
||||
character_id,
|
||||
view_distance,
|
||||
uuid,
|
||||
}
|
||||
}
|
||||
pub fn new(alias: String, uuid: Uuid) -> Self { Self { alias, uuid } }
|
||||
|
||||
pub fn is_valid(&self) -> bool { Self::alias_is_valid(&self.alias) }
|
||||
|
||||
|
@ -86,21 +86,21 @@ impl ClientMsg {
|
||||
&self,
|
||||
c_type: ClientType,
|
||||
registered: bool,
|
||||
in_game: Option<super::ClientInGame>,
|
||||
presence: Option<super::PresenceKind>,
|
||||
) -> bool {
|
||||
match self {
|
||||
ClientMsg::Type(t) => c_type == *t,
|
||||
ClientMsg::Register(_) => !registered && in_game.is_none(),
|
||||
ClientMsg::Register(_) => !registered && presence.is_none(),
|
||||
ClientMsg::General(g) => {
|
||||
registered
|
||||
&& match g {
|
||||
ClientGeneral::RequestCharacterList
|
||||
| ClientGeneral::CreateCharacter { .. }
|
||||
| ClientGeneral::DeleteCharacter(_) => {
|
||||
c_type != ClientType::ChatOnly && in_game.is_none()
|
||||
c_type != ClientType::ChatOnly && presence.is_none()
|
||||
},
|
||||
ClientGeneral::Character(_) | ClientGeneral::Spectate => {
|
||||
c_type == ClientType::Game && in_game.is_none()
|
||||
c_type == ClientType::Game && presence.is_none()
|
||||
},
|
||||
//Only in game
|
||||
ClientGeneral::ControllerInputs(_)
|
||||
@ -115,7 +115,7 @@ impl ClientMsg {
|
||||
| ClientGeneral::UnlockSkill(_)
|
||||
| ClientGeneral::RefundSkill(_)
|
||||
| ClientGeneral::UnlockSkillGroup(_) => {
|
||||
c_type == ClientType::Game && in_game.is_some()
|
||||
c_type == ClientType::Game && presence.is_some()
|
||||
},
|
||||
//Always possible
|
||||
ClientGeneral::ChatMsg(_) | ClientGeneral::Terminate => true,
|
||||
|
@ -13,12 +13,13 @@ pub use self::{
|
||||
},
|
||||
world_msg::WorldMapMsg,
|
||||
};
|
||||
use crate::character::CharacterId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ClientInGame {
|
||||
pub enum PresenceKind {
|
||||
Spectator,
|
||||
Character,
|
||||
Character(CharacterId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
|
@ -182,11 +182,11 @@ impl ServerMsg {
|
||||
&self,
|
||||
c_type: ClientType,
|
||||
registered: bool,
|
||||
in_game: Option<super::ClientInGame>,
|
||||
presence: Option<super::PresenceKind>,
|
||||
) -> bool {
|
||||
match self {
|
||||
ServerMsg::Info(_) | ServerMsg::Init(_) | ServerMsg::RegisterAnswer(_) => {
|
||||
!registered && in_game.is_none()
|
||||
!registered && presence.is_none()
|
||||
},
|
||||
ServerMsg::General(g) => {
|
||||
registered
|
||||
@ -195,10 +195,10 @@ impl ServerMsg {
|
||||
ServerGeneral::CharacterDataLoadError(_)
|
||||
| ServerGeneral::CharacterListUpdate(_)
|
||||
| ServerGeneral::CharacterActionError(_) => {
|
||||
c_type != ClientType::ChatOnly && in_game.is_none()
|
||||
c_type != ClientType::ChatOnly && presence.is_none()
|
||||
},
|
||||
ServerGeneral::CharacterSuccess => {
|
||||
c_type == ClientType::Game && in_game.is_none()
|
||||
c_type == ClientType::Game && presence.is_none()
|
||||
},
|
||||
//Ingame related
|
||||
ServerGeneral::GroupUpdate(_)
|
||||
@ -212,7 +212,7 @@ impl ServerMsg {
|
||||
| ServerGeneral::SetViewDistance(_)
|
||||
| ServerGeneral::Outcomes(_)
|
||||
| ServerGeneral::Knockback(_) => {
|
||||
c_type == ClientType::Game && in_game.is_some()
|
||||
c_type == ClientType::Game && presence.is_some()
|
||||
},
|
||||
// Always possible
|
||||
ServerGeneral::PlayerListUpdate(_)
|
||||
|
@ -155,7 +155,7 @@ impl ShutdownCoordinator {
|
||||
/// Logs and sends a message to all connected clients
|
||||
fn send_msg(server: &mut Server, msg: String) {
|
||||
info!("{}", &msg);
|
||||
server.notify_registered_clients(ChatType::CommandError.server_msg(msg));
|
||||
server.notify_players(ChatType::CommandError.server_msg(msg));
|
||||
}
|
||||
|
||||
/// Converts a `Duration` into text in the format XsXm for example 1 minute
|
||||
|
@ -1,14 +1,16 @@
|
||||
use common::msg::{ClientInGame, ClientType};
|
||||
use hashbrown::HashSet;
|
||||
use common::msg::ClientType;
|
||||
use network::Participant;
|
||||
use specs::{Component, FlaggedStorage};
|
||||
use specs::Component;
|
||||
use specs_idvs::IdvStorage;
|
||||
use vek::*;
|
||||
|
||||
/// Client handles ALL network related information of everything that connects
|
||||
/// to the server Client DOES NOT handle game states
|
||||
/// Client DOES NOT handle network information that is only relevant to some
|
||||
/// "things" connecting to the server (there is currently no such case). First a
|
||||
/// Client connects to the game, when it registers, it gets the `Player`
|
||||
/// component, when he enters the game he gets the `InGame` component.
|
||||
pub struct Client {
|
||||
pub registered: bool,
|
||||
pub client_type: ClientType,
|
||||
pub in_game: Option<ClientInGame>,
|
||||
pub participant: Option<Participant>,
|
||||
pub last_ping: f64,
|
||||
pub login_msg_sent: bool,
|
||||
@ -16,20 +18,5 @@ pub struct Client {
|
||||
}
|
||||
|
||||
impl Component for Client {
|
||||
type Storage = FlaggedStorage<Self, IdvStorage<Self>>;
|
||||
}
|
||||
|
||||
// Distance from fuzzy_chunk before snapping to current chunk
|
||||
pub const CHUNK_FUZZ: u32 = 2;
|
||||
// Distance out of the range of a region before removing it from subscriptions
|
||||
pub const REGION_FUZZ: u32 = 16;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RegionSubscription {
|
||||
pub fuzzy_chunk: Vec2<i32>,
|
||||
pub regions: HashSet<Vec2<i32>>,
|
||||
}
|
||||
|
||||
impl Component for RegionSubscription {
|
||||
type Storage = FlaggedStorage<Self, IdvStorage<Self>>;
|
||||
type Storage = IdvStorage<Self>;
|
||||
}
|
||||
|
@ -510,11 +510,11 @@ fn handle_alias(
|
||||
*uid,
|
||||
player.alias.clone(),
|
||||
));
|
||||
server.state.notify_registered_clients(msg);
|
||||
server.state.notify_players(msg);
|
||||
|
||||
// Announce alias change if target has a Body.
|
||||
if ecs.read_storage::<comp::Body>().get(target).is_some() {
|
||||
server.state.notify_registered_clients(
|
||||
server.state.notify_players(
|
||||
ChatType::CommandInfo
|
||||
.server_msg(format!("{} is now known as {}.", old_alias, player.alias)),
|
||||
);
|
||||
@ -1214,7 +1214,7 @@ fn handle_adminify(
|
||||
.expect("Player should have uid"),
|
||||
is_admin,
|
||||
));
|
||||
server.state.notify_registered_clients(msg);
|
||||
server.state.notify_players(msg);
|
||||
},
|
||||
None => {
|
||||
server.notify_client(
|
||||
@ -1671,11 +1671,9 @@ fn handle_set_level(
|
||||
.read_storage::<Uid>()
|
||||
.get(player)
|
||||
.expect("Failed to get uid for player");
|
||||
server
|
||||
.state
|
||||
.notify_registered_clients(ServerGeneral::PlayerListUpdate(
|
||||
PlayerListUpdate::LevelChange(uid, lvl),
|
||||
));
|
||||
server.state.notify_players(ServerGeneral::PlayerListUpdate(
|
||||
PlayerListUpdate::LevelChange(uid, lvl),
|
||||
));
|
||||
|
||||
if let Some(stats) = server
|
||||
.state
|
||||
|
@ -137,9 +137,7 @@ impl ConnectionHandler {
|
||||
};
|
||||
|
||||
let client = Client {
|
||||
registered: false,
|
||||
client_type,
|
||||
in_game: None,
|
||||
participant: Some(participant),
|
||||
last_ping: server_data.time,
|
||||
login_msg_sent: false,
|
||||
|
@ -200,9 +200,8 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
||||
| HealthSource::Healing { by: _ }
|
||||
| HealthSource::Unknown => KillSource::Other,
|
||||
};
|
||||
state.notify_registered_clients(
|
||||
comp::ChatType::Kill(kill_source, *uid).server_msg("".to_string()),
|
||||
);
|
||||
state
|
||||
.notify_players(comp::ChatType::Kill(kill_source, *uid).server_msg("".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -668,11 +667,9 @@ pub fn handle_level_up(server: &mut Server, entity: EcsEntity, new_level: u32) {
|
||||
.get(entity)
|
||||
.expect("Failed to fetch uid component for entity.");
|
||||
|
||||
server
|
||||
.state
|
||||
.notify_registered_clients(ServerGeneral::PlayerListUpdate(
|
||||
PlayerListUpdate::LevelChange(*uid, new_level),
|
||||
));
|
||||
server.state.notify_players(ServerGeneral::PlayerListUpdate(
|
||||
PlayerListUpdate::LevelChange(*uid, new_level),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::BuffChange) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::{
|
||||
client::{Client, RegionSubscription},
|
||||
client::Client,
|
||||
presence::RegionSubscription,
|
||||
streams::{
|
||||
CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream,
|
||||
},
|
||||
|
@ -3,6 +3,7 @@ use crate::{
|
||||
client::Client,
|
||||
login_provider::LoginProvider,
|
||||
persistence,
|
||||
presence::Presence,
|
||||
state_ext::StateExt,
|
||||
streams::{
|
||||
CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream,
|
||||
@ -12,7 +13,7 @@ use crate::{
|
||||
use common::{
|
||||
comp,
|
||||
comp::{group, Player},
|
||||
msg::{PlayerListUpdate, ServerGeneral},
|
||||
msg::{PlayerListUpdate, PresenceKind, ServerGeneral},
|
||||
span,
|
||||
sync::{Uid, UidAllocator},
|
||||
};
|
||||
@ -37,7 +38,7 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) {
|
||||
.cloned();
|
||||
|
||||
if let Some((
|
||||
mut client,
|
||||
client,
|
||||
uid,
|
||||
player,
|
||||
general_stream,
|
||||
@ -60,7 +61,6 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) {
|
||||
))
|
||||
})() {
|
||||
// Tell client its request was successful
|
||||
client.in_game = None;
|
||||
in_game_stream.send_fallible(ServerGeneral::ExitInGameSuccess);
|
||||
|
||||
let entity_builder = state
|
||||
@ -163,9 +163,9 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event
|
||||
state.read_storage::<Uid>().get(entity),
|
||||
state.read_storage::<comp::Player>().get(entity),
|
||||
) {
|
||||
state.notify_registered_clients(comp::ChatType::Offline(*uid).server_msg(""));
|
||||
state.notify_players(comp::ChatType::Offline(*uid).server_msg(""));
|
||||
|
||||
state.notify_registered_clients(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Remove(
|
||||
state.notify_players(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Remove(
|
||||
*uid,
|
||||
)));
|
||||
}
|
||||
@ -177,8 +177,8 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event
|
||||
}
|
||||
|
||||
// Sync the player's character data to the database
|
||||
if let (Some(player), Some(stats), Some(inventory), Some(loadout), updater) = (
|
||||
state.read_storage::<Player>().get(entity),
|
||||
if let (Some(presences), Some(stats), Some(inventory), Some(loadout), updater) = (
|
||||
state.read_storage::<Presence>().get(entity),
|
||||
state.read_storage::<comp::Stats>().get(entity),
|
||||
state.read_storage::<comp::Inventory>().get(entity),
|
||||
state.read_storage::<comp::Loadout>().get(entity),
|
||||
@ -186,7 +186,7 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event
|
||||
.ecs()
|
||||
.read_resource::<persistence::character_updater::CharacterUpdater>(),
|
||||
) {
|
||||
if let Some(character_id) = player.character_id {
|
||||
if let PresenceKind::Character(character_id) = presences.kind {
|
||||
updater.update(character_id, stats, inventory, loadout);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ pub mod input;
|
||||
pub mod login_provider;
|
||||
pub mod metrics;
|
||||
pub mod persistence;
|
||||
pub mod presence;
|
||||
pub mod settings;
|
||||
pub mod state_ext;
|
||||
pub mod streams;
|
||||
@ -35,11 +36,12 @@ pub use crate::{
|
||||
use crate::{
|
||||
alias_validator::AliasValidator,
|
||||
chunk_generator::ChunkGenerator,
|
||||
client::{Client, RegionSubscription},
|
||||
client::Client,
|
||||
cmd::ChatCommandExt,
|
||||
connection_handler::ConnectionHandler,
|
||||
data_dir::DataDir,
|
||||
login_provider::LoginProvider,
|
||||
presence::{Presence, RegionSubscription},
|
||||
state_ext::StateExt,
|
||||
streams::{
|
||||
CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream,
|
||||
@ -190,6 +192,7 @@ impl Server {
|
||||
// Server-only components
|
||||
state.ecs_mut().register::<RegionSubscription>();
|
||||
state.ecs_mut().register::<Client>();
|
||||
state.ecs_mut().register::<Presence>();
|
||||
state.ecs_mut().register::<GeneralStream>();
|
||||
state.ecs_mut().register::<PingStream>();
|
||||
state.ecs_mut().register::<RegisterStream>();
|
||||
@ -971,9 +974,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_registered_clients(&mut self, msg: ServerGeneral) {
|
||||
self.state.notify_registered_clients(msg);
|
||||
}
|
||||
pub fn notify_players(&mut self, msg: ServerGeneral) { self.state.notify_players(msg); }
|
||||
|
||||
pub fn generate_chunk(&mut self, entity: EcsEntity, key: Vec2<i32>) {
|
||||
self.state
|
||||
@ -1051,7 +1052,7 @@ impl Server {
|
||||
impl Drop for Server {
|
||||
fn drop(&mut self) {
|
||||
self.state
|
||||
.notify_registered_clients(ServerGeneral::Disconnect(DisconnectReason::Shutdown));
|
||||
.notify_players(ServerGeneral::Disconnect(DisconnectReason::Shutdown));
|
||||
}
|
||||
}
|
||||
|
||||
|
40
server/src/presence.rs
Normal file
40
server/src/presence.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use common::msg::PresenceKind;
|
||||
use hashbrown::HashSet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::{Component, FlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Presence {
|
||||
pub view_distance: u32,
|
||||
pub kind: PresenceKind,
|
||||
}
|
||||
|
||||
impl Presence {
|
||||
pub fn new(view_distance: u32, kind: PresenceKind) -> Self {
|
||||
Self {
|
||||
view_distance,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Presence {
|
||||
type Storage = FlaggedStorage<Self, IdvStorage<Self>>;
|
||||
}
|
||||
|
||||
// Distance from fuzzy_chunk before snapping to current chunk
|
||||
pub const CHUNK_FUZZ: u32 = 2;
|
||||
// Distance out of the range of a region before removing it from subscriptions
|
||||
pub const REGION_FUZZ: u32 = 16;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RegionSubscription {
|
||||
pub fuzzy_chunk: Vec2<i32>,
|
||||
pub regions: HashSet<Vec2<i32>>,
|
||||
}
|
||||
|
||||
impl Component for RegionSubscription {
|
||||
type Storage = FlaggedStorage<Self, IdvStorage<Self>>;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
client::Client,
|
||||
persistence::PersistedComponents,
|
||||
presence::Presence,
|
||||
streams::{CharacterScreenStream, GeneralStream, GetStream, InGameStream},
|
||||
sys::sentinel::DeletedEntities,
|
||||
SpawnPoint,
|
||||
@ -9,7 +9,7 @@ use common::{
|
||||
character::CharacterId,
|
||||
comp,
|
||||
effect::Effect,
|
||||
msg::{CharacterInfo, ClientInGame, PlayerListUpdate, ServerGeneral},
|
||||
msg::{CharacterInfo, PlayerListUpdate, PresenceKind, ServerGeneral},
|
||||
state::State,
|
||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
||||
util::Dir,
|
||||
@ -63,7 +63,7 @@ pub trait StateExt {
|
||||
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents);
|
||||
/// Iterates over registered clients and send each `ServerMsg`
|
||||
fn send_chat(&self, msg: comp::UnresolvedChatMsg);
|
||||
fn notify_registered_clients(&self, msg: ServerGeneral);
|
||||
fn notify_players(&self, msg: ServerGeneral);
|
||||
fn notify_in_game_clients(&self, msg: ServerGeneral);
|
||||
/// Delete an entity, recording the deletion in [`DeletedEntities`]
|
||||
fn delete_entity_recorded(
|
||||
@ -212,28 +212,19 @@ impl StateExt for State {
|
||||
// Make sure physics components are updated
|
||||
self.write_component(entity, comp::ForceUpdate);
|
||||
|
||||
// Set the character id for the player
|
||||
// TODO this results in a warning in the console: "Error modifying synced
|
||||
// component, it doesn't seem to exist"
|
||||
// It appears to be caused by the player not yet existing on the client at this
|
||||
// point, despite being able to write the data on the server
|
||||
self.ecs()
|
||||
.write_storage::<comp::Player>()
|
||||
.get_mut(entity)
|
||||
.map(|player| {
|
||||
player.character_id = Some(character_id);
|
||||
});
|
||||
const INITIAL_VD: u32 = 5; //will be changed after login
|
||||
self.write_component(
|
||||
entity,
|
||||
Presence::new(INITIAL_VD, PresenceKind::Character(character_id)),
|
||||
);
|
||||
|
||||
// Tell the client its request was successful.
|
||||
if let Some(client) = self.ecs().write_storage::<Client>().get_mut(entity) {
|
||||
if let Some(character_screen_stream) = self
|
||||
.ecs()
|
||||
.write_storage::<CharacterScreenStream>()
|
||||
.get_mut(entity)
|
||||
{
|
||||
client.in_game = Some(ClientInGame::Character);
|
||||
character_screen_stream.send_fallible(ServerGeneral::CharacterSuccess);
|
||||
}
|
||||
if let Some(character_screen_stream) = self
|
||||
.ecs()
|
||||
.write_storage::<CharacterScreenStream>()
|
||||
.get_mut(entity)
|
||||
{
|
||||
character_screen_stream.send_fallible(ServerGeneral::CharacterSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,7 +233,7 @@ impl StateExt for State {
|
||||
|
||||
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
|
||||
// Notify clients of a player list update
|
||||
self.notify_registered_clients(ServerGeneral::PlayerListUpdate(
|
||||
self.notify_players(ServerGeneral::PlayerListUpdate(
|
||||
PlayerListUpdate::SelectedCharacter(player_uid, CharacterInfo {
|
||||
name: String::from(&stats.name),
|
||||
level: stats.level.level(),
|
||||
@ -287,9 +278,7 @@ impl StateExt for State {
|
||||
| comp::ChatType::Loot
|
||||
| comp::ChatType::Kill(_, _)
|
||||
| comp::ChatType::Meta
|
||||
| comp::ChatType::World(_) => {
|
||||
self.notify_registered_clients(ServerGeneral::ChatMsg(resolved_msg))
|
||||
},
|
||||
| comp::ChatType::World(_) => self.notify_players(ServerGeneral::ChatMsg(resolved_msg)),
|
||||
comp::ChatType::Online(u) => {
|
||||
for (general_stream, uid) in (
|
||||
&mut ecs.write_storage::<GeneralStream>(),
|
||||
@ -389,14 +378,13 @@ impl StateExt for State {
|
||||
}
|
||||
|
||||
/// Sends the message to all connected clients
|
||||
fn notify_registered_clients(&self, msg: ServerGeneral) {
|
||||
fn notify_players(&self, msg: ServerGeneral) {
|
||||
let mut lazy_msg = None;
|
||||
for (general_stream, _) in (
|
||||
&mut self.ecs().write_storage::<GeneralStream>(),
|
||||
&self.ecs().read_storage::<Client>(),
|
||||
&self.ecs().read_storage::<comp::Player>(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(_, c)| c.registered)
|
||||
{
|
||||
if lazy_msg.is_none() {
|
||||
lazy_msg = Some(general_stream.prepare(&msg));
|
||||
@ -412,10 +400,9 @@ impl StateExt for State {
|
||||
let mut lazy_msg = None;
|
||||
for (general_stream, _) in (
|
||||
&mut self.ecs().write_storage::<GeneralStream>(),
|
||||
&self.ecs().read_storage::<Client>(),
|
||||
&self.ecs().read_storage::<Presence>(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(_, c)| c.in_game.is_some())
|
||||
{
|
||||
if lazy_msg.is_none() {
|
||||
lazy_msg = Some(general_stream.prepare(&msg));
|
||||
|
@ -3,12 +3,13 @@ use super::{
|
||||
SysTimer,
|
||||
};
|
||||
use crate::{
|
||||
client::{Client, RegionSubscription},
|
||||
client::Client,
|
||||
presence::{Presence, RegionSubscription},
|
||||
streams::{GeneralStream, GetStream, InGameStream},
|
||||
Tick,
|
||||
};
|
||||
use common::{
|
||||
comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Player, Pos, Vel},
|
||||
comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Pos, Vel},
|
||||
msg::ServerGeneral,
|
||||
outcome::Outcome,
|
||||
region::{Event as RegionEvent, RegionMap},
|
||||
@ -39,7 +40,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Ori>,
|
||||
ReadStorage<'a, Inventory>,
|
||||
ReadStorage<'a, RegionSubscription>,
|
||||
ReadStorage<'a, Player>,
|
||||
ReadStorage<'a, Presence>,
|
||||
WriteStorage<'a, Last<Pos>>,
|
||||
WriteStorage<'a, Last<Vel>>,
|
||||
WriteStorage<'a, Last<Ori>>,
|
||||
@ -68,7 +69,7 @@ impl<'a> System<'a> for Sys {
|
||||
orientations,
|
||||
inventories,
|
||||
subscriptions,
|
||||
players,
|
||||
presences,
|
||||
mut last_pos,
|
||||
mut last_vel,
|
||||
mut last_ori,
|
||||
@ -112,6 +113,7 @@ impl<'a> System<'a> for Sys {
|
||||
let mut subscribers = (
|
||||
&mut clients,
|
||||
&entities,
|
||||
presences.maybe(),
|
||||
&subscriptions,
|
||||
&positions,
|
||||
&mut in_game_streams,
|
||||
@ -119,8 +121,16 @@ impl<'a> System<'a> for Sys {
|
||||
)
|
||||
.join()
|
||||
.filter_map(
|
||||
|(client, entity, subscription, pos, in_game_stream, general_stream)| {
|
||||
if client.in_game.is_some() && subscription.regions.contains(&key) {
|
||||
|(
|
||||
client,
|
||||
entity,
|
||||
presence,
|
||||
subscription,
|
||||
pos,
|
||||
in_game_stream,
|
||||
general_stream,
|
||||
)| {
|
||||
if presence.is_some() && subscription.regions.contains(&key) {
|
||||
Some((
|
||||
client,
|
||||
&subscription.regions,
|
||||
@ -339,10 +349,10 @@ impl<'a> System<'a> for Sys {
|
||||
// Handle entity deletion in regions that don't exist in RegionMap
|
||||
// (theoretically none)
|
||||
for (region_key, deleted) in deleted_entities.take_remaining_deleted() {
|
||||
for general_stream in (&mut clients, &subscriptions, &mut general_streams)
|
||||
for general_stream in (presences.maybe(), &subscriptions, &mut general_streams)
|
||||
.join()
|
||||
.filter_map(|(client, subscription, general_stream)| {
|
||||
if client.in_game.is_some() && subscription.regions.contains(®ion_key) {
|
||||
.filter_map(|(presence, subscription, general_stream)| {
|
||||
if presence.is_some() && subscription.regions.contains(®ion_key) {
|
||||
Some(general_stream)
|
||||
} else {
|
||||
None
|
||||
@ -368,13 +378,14 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Sync outcomes
|
||||
for (player, pos, in_game_stream) in
|
||||
(&players, positions.maybe(), &mut in_game_streams).join()
|
||||
for (presence, pos, in_game_stream) in
|
||||
(presences.maybe(), positions.maybe(), &mut in_game_streams).join()
|
||||
{
|
||||
let is_near = |o_pos: Vec3<f32>| {
|
||||
pos.zip_with(player.view_distance, |pos, vd| {
|
||||
pos.zip_with(presence, |pos, presence| {
|
||||
pos.0.xy().distance_squared(o_pos.xy())
|
||||
< (vd as f32 * TerrainChunkSize::RECT_SIZE.x as f32).powf(2.0)
|
||||
< (presence.view_distance as f32 * TerrainChunkSize::RECT_SIZE.x as f32)
|
||||
.powf(2.0)
|
||||
})
|
||||
};
|
||||
|
||||
|
@ -4,19 +4,20 @@ use crate::{
|
||||
character_creator,
|
||||
client::Client,
|
||||
persistence::character_loader::CharacterLoader,
|
||||
presence::Presence,
|
||||
streams::{CharacterScreenStream, GeneralStream, GetStream},
|
||||
EditableSettings,
|
||||
};
|
||||
use common::{
|
||||
comp::{ChatType, Player, UnresolvedChatMsg},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{ClientGeneral, ClientInGame, ServerGeneral},
|
||||
msg::{ClientGeneral, ServerGeneral},
|
||||
span,
|
||||
state::Time,
|
||||
sync::Uid,
|
||||
};
|
||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage};
|
||||
use tracing::debug;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
impl Sys {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -30,64 +31,65 @@ impl Sys {
|
||||
character_loader: &ReadExpect<'_, CharacterLoader>,
|
||||
uids: &ReadStorage<'_, Uid>,
|
||||
players: &ReadStorage<'_, Player>,
|
||||
presences: &ReadStorage<'_, Presence>,
|
||||
editable_settings: &ReadExpect<'_, EditableSettings>,
|
||||
alias_validator: &ReadExpect<'_, AliasValidator>,
|
||||
msg: ClientGeneral,
|
||||
) -> Result<(), crate::error::Error> {
|
||||
match msg {
|
||||
// Request spectator state
|
||||
ClientGeneral::Spectate if client.registered => {
|
||||
client.in_game = Some(ClientInGame::Spectator)
|
||||
ClientGeneral::Spectate => {
|
||||
if players.contains(entity) {
|
||||
warn!("Spectator mode not yet implemented on server");
|
||||
} else {
|
||||
debug!("dropped Spectate msg from unregistered client")
|
||||
}
|
||||
},
|
||||
ClientGeneral::Spectate => debug!("dropped Spectate msg from unregistered client"),
|
||||
ClientGeneral::Character(character_id)
|
||||
if client.registered && client.in_game.is_none() =>
|
||||
{
|
||||
ClientGeneral::Character(character_id) => {
|
||||
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,
|
||||
);
|
||||
if presences.contains(entity) {
|
||||
debug!("player already ingame, aborting");
|
||||
} else {
|
||||
// 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,
|
||||
});
|
||||
// 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() {
|
||||
general_stream
|
||||
.send(ChatType::CommandInfo.server_msg(String::from(
|
||||
// Give the player a welcome message
|
||||
if !editable_settings.server_description.is_empty() {
|
||||
general_stream.send(ChatType::CommandInfo.server_msg(String::from(
|
||||
&*editable_settings.server_description,
|
||||
)))?;
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
}));
|
||||
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;
|
||||
client.login_msg_sent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!("Client is not yet registered");
|
||||
character_screen_stream.send(ServerGeneral::CharacterDataLoadError(
|
||||
String::from("Failed to fetch player entity"),
|
||||
))?
|
||||
}
|
||||
}
|
||||
ClientGeneral::Character(_) => {
|
||||
let registered = client.registered;
|
||||
let in_game = client.in_game;
|
||||
debug!(?registered, ?in_game, "dropped Character msg from client");
|
||||
},
|
||||
ClientGeneral::RequestCharacterList => {
|
||||
if let Some(player) = players.get(entity) {
|
||||
@ -138,6 +140,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Uid>,
|
||||
WriteStorage<'a, Client>,
|
||||
ReadStorage<'a, Player>,
|
||||
ReadStorage<'a, Presence>,
|
||||
WriteStorage<'a, CharacterScreenStream>,
|
||||
WriteStorage<'a, GeneralStream>,
|
||||
ReadExpect<'a, EditableSettings>,
|
||||
@ -155,6 +158,7 @@ impl<'a> System<'a> for Sys {
|
||||
uids,
|
||||
mut clients,
|
||||
players,
|
||||
presences,
|
||||
mut character_screen_streams,
|
||||
mut general_streams,
|
||||
editable_settings,
|
||||
@ -187,6 +191,7 @@ impl<'a> System<'a> for Sys {
|
||||
&character_loader,
|
||||
&uids,
|
||||
&players,
|
||||
&presences,
|
||||
&editable_settings,
|
||||
&alias_validator,
|
||||
msg,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::super::SysTimer;
|
||||
use crate::{client::Client, metrics::PlayerMetrics, streams::GeneralStream};
|
||||
use common::{
|
||||
comp::{ChatMode, UnresolvedChatMsg},
|
||||
comp::{ChatMode, Player, UnresolvedChatMsg},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{validate_chat_msg, ChatMsgValidationError, ClientGeneral, MAX_BYTES_CHAT_MSG},
|
||||
span,
|
||||
@ -18,6 +18,7 @@ impl Sys {
|
||||
new_chat_msgs: &mut Vec<(Option<specs::Entity>, UnresolvedChatMsg)>,
|
||||
entity: specs::Entity,
|
||||
client: &mut Client,
|
||||
player: Option<&Player>,
|
||||
player_metrics: &ReadExpect<'_, PlayerMetrics>,
|
||||
uids: &ReadStorage<'_, Uid>,
|
||||
chat_modes: &ReadStorage<'_, ChatMode>,
|
||||
@ -25,7 +26,7 @@ impl Sys {
|
||||
) -> Result<(), crate::error::Error> {
|
||||
match msg {
|
||||
ClientGeneral::ChatMsg(message) => {
|
||||
if client.registered {
|
||||
if player.is_some() {
|
||||
match validate_chat_msg(&message) {
|
||||
Ok(()) => {
|
||||
if let Some(from) = uids.get(entity) {
|
||||
@ -71,6 +72,7 @@ impl<'a> System<'a> for Sys {
|
||||
Write<'a, SysTimer<Self>>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, ChatMode>,
|
||||
ReadStorage<'a, Player>,
|
||||
WriteStorage<'a, Client>,
|
||||
WriteStorage<'a, GeneralStream>,
|
||||
);
|
||||
@ -85,6 +87,7 @@ impl<'a> System<'a> for Sys {
|
||||
mut timer,
|
||||
uids,
|
||||
chat_modes,
|
||||
players,
|
||||
mut clients,
|
||||
mut general_streams,
|
||||
): Self::SystemData,
|
||||
@ -95,8 +98,13 @@ impl<'a> System<'a> for Sys {
|
||||
let mut server_emitter = server_event_bus.emitter();
|
||||
let mut new_chat_msgs = Vec::new();
|
||||
|
||||
for (entity, client, general_stream) in
|
||||
(&entities, &mut clients, &mut general_streams).join()
|
||||
for (entity, client, player, general_stream) in (
|
||||
&entities,
|
||||
&mut clients,
|
||||
(&players).maybe(),
|
||||
&mut general_streams,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let res = super::try_recv_all(general_stream, |_, msg| {
|
||||
Self::handle_general_msg(
|
||||
@ -104,6 +112,7 @@ impl<'a> System<'a> for Sys {
|
||||
&mut new_chat_msgs,
|
||||
entity,
|
||||
client,
|
||||
player,
|
||||
&player_metrics,
|
||||
&uids,
|
||||
&chat_modes,
|
||||
|
@ -2,13 +2,14 @@ use super::super::SysTimer;
|
||||
use crate::{
|
||||
client::Client,
|
||||
metrics::NetworkRequestMetrics,
|
||||
presence::Presence,
|
||||
streams::{GetStream, InGameStream},
|
||||
Settings,
|
||||
};
|
||||
use common::{
|
||||
comp::{CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, Stats, Vel},
|
||||
comp::{CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Pos, Stats, Vel},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{ClientGeneral, ClientInGame, ServerGeneral},
|
||||
msg::{ClientGeneral, PresenceKind, ServerGeneral},
|
||||
span,
|
||||
state::{BlockChange, Time},
|
||||
terrain::{TerrainChunkSize, TerrainGrid},
|
||||
@ -22,7 +23,8 @@ impl Sys {
|
||||
fn handle_client_in_game_msg(
|
||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
||||
entity: specs::Entity,
|
||||
client: &mut Client,
|
||||
_client: &Client,
|
||||
maybe_presence: &mut Option<&mut Presence>,
|
||||
in_game_stream: &mut InGameStream,
|
||||
terrain: &ReadExpect<'_, TerrainGrid>,
|
||||
network_metrics: &ReadExpect<'_, NetworkRequestMetrics>,
|
||||
@ -33,35 +35,33 @@ impl Sys {
|
||||
positions: &mut WriteStorage<'_, Pos>,
|
||||
velocities: &mut WriteStorage<'_, Vel>,
|
||||
orientations: &mut WriteStorage<'_, Ori>,
|
||||
players: &mut WriteStorage<'_, Player>,
|
||||
controllers: &mut WriteStorage<'_, Controller>,
|
||||
settings: &Read<'_, Settings>,
|
||||
msg: ClientGeneral,
|
||||
) -> Result<(), crate::error::Error> {
|
||||
if client.in_game.is_none() {
|
||||
debug!(?entity, "client is not in_game, ignoring msg");
|
||||
trace!(?msg, "ignored msg content");
|
||||
if matches!(msg, ClientGeneral::TerrainChunkRequest{ .. }) {
|
||||
network_metrics.chunks_request_dropped.inc();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let presence = match maybe_presence {
|
||||
Some(g) => g,
|
||||
None => {
|
||||
debug!(?entity, "client is not in_game, ignoring msg");
|
||||
trace!(?msg, "ignored msg content");
|
||||
if matches!(msg, ClientGeneral::TerrainChunkRequest{ .. }) {
|
||||
network_metrics.chunks_request_dropped.inc();
|
||||
}
|
||||
return Ok(());
|
||||
},
|
||||
};
|
||||
match msg {
|
||||
// Go back to registered state (char selection screen)
|
||||
ClientGeneral::ExitInGame => {
|
||||
client.in_game = None;
|
||||
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
||||
in_game_stream.send(ServerGeneral::ExitInGameSuccess)?;
|
||||
*maybe_presence = None;
|
||||
},
|
||||
ClientGeneral::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),
|
||||
)
|
||||
});
|
||||
presence.view_distance = settings
|
||||
.max_view_distance
|
||||
.map(|max| view_distance.min(max))
|
||||
.unwrap_or(view_distance);
|
||||
|
||||
//correct client if its VD is to high
|
||||
if settings
|
||||
@ -75,14 +75,14 @@ impl Sys {
|
||||
}
|
||||
},
|
||||
ClientGeneral::ControllerInputs(inputs) => {
|
||||
if let Some(ClientInGame::Character) = client.in_game {
|
||||
if matches!(presence.kind, PresenceKind::Character(_)) {
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.inputs.update_with_new(inputs);
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientGeneral::ControlEvent(event) => {
|
||||
if let Some(ClientInGame::Character) = client.in_game {
|
||||
if matches!(presence.kind, PresenceKind::Character(_)) {
|
||||
// Skip respawn if client entity is alive
|
||||
if let ControlEvent::Respawn = event {
|
||||
if stats.get(entity).map_or(true, |s| !s.is_dead) {
|
||||
@ -96,21 +96,20 @@ impl Sys {
|
||||
}
|
||||
},
|
||||
ClientGeneral::ControlAction(event) => {
|
||||
if let Some(ClientInGame::Character) = client.in_game {
|
||||
if matches!(presence.kind, PresenceKind::Character(_)) {
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.actions.push(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientGeneral::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);
|
||||
}
|
||||
if matches!(presence.kind, PresenceKind::Character(_))
|
||||
&& 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);
|
||||
}
|
||||
},
|
||||
ClientGeneral::BreakBlock(pos) => {
|
||||
@ -124,13 +123,10 @@ impl Sys {
|
||||
}
|
||||
},
|
||||
ClientGeneral::TerrainChunkRequest { key } => {
|
||||
let in_vd = if let (Some(view_distance), Some(pos)) = (
|
||||
players.get(entity).and_then(|p| p.view_distance),
|
||||
positions.get(entity),
|
||||
) {
|
||||
let in_vd = if let Some(pos) = 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())
|
||||
) < (presence.view_distance as f64 - 1.0 + 2.5 * 2.0_f64.sqrt())
|
||||
* TerrainChunkSize::RECT_SIZE.x as f64
|
||||
} else {
|
||||
true
|
||||
@ -192,7 +188,7 @@ impl<'a> System<'a> for Sys {
|
||||
WriteStorage<'a, Pos>,
|
||||
WriteStorage<'a, Vel>,
|
||||
WriteStorage<'a, Ori>,
|
||||
WriteStorage<'a, Player>,
|
||||
WriteStorage<'a, Presence>,
|
||||
WriteStorage<'a, Client>,
|
||||
WriteStorage<'a, InGameStream>,
|
||||
WriteStorage<'a, Controller>,
|
||||
@ -215,7 +211,7 @@ impl<'a> System<'a> for Sys {
|
||||
mut positions,
|
||||
mut velocities,
|
||||
mut orientations,
|
||||
mut players,
|
||||
mut presences,
|
||||
mut clients,
|
||||
mut in_game_streams,
|
||||
mut controllers,
|
||||
@ -227,14 +223,20 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let mut server_emitter = server_event_bus.emitter();
|
||||
|
||||
for (entity, client, in_game_stream) in
|
||||
(&entities, &mut clients, &mut in_game_streams).join()
|
||||
for (entity, client, mut presence, in_game_stream) in (
|
||||
&entities,
|
||||
&mut clients,
|
||||
(&mut presences).maybe(),
|
||||
&mut in_game_streams,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let res = super::try_recv_all(in_game_stream, |in_game_stream, msg| {
|
||||
Self::handle_client_in_game_msg(
|
||||
&mut server_emitter,
|
||||
entity,
|
||||
client,
|
||||
&mut presence,
|
||||
in_game_stream,
|
||||
&terrain,
|
||||
&network_metrics,
|
||||
@ -245,7 +247,6 @@ impl<'a> System<'a> for Sys {
|
||||
&mut positions,
|
||||
&mut velocities,
|
||||
&mut orientations,
|
||||
&mut players,
|
||||
&mut controllers,
|
||||
&settings,
|
||||
msg,
|
||||
|
@ -27,7 +27,7 @@ impl Sys {
|
||||
player_list: &HashMap<Uid, PlayerInfo>,
|
||||
new_players: &mut Vec<specs::Entity>,
|
||||
entity: specs::Entity,
|
||||
client: &mut Client,
|
||||
_client: &mut Client,
|
||||
register_stream: &mut RegisterStream,
|
||||
general_stream: &mut GeneralStream,
|
||||
player_metrics: &ReadExpect<'_, PlayerMetrics>,
|
||||
@ -50,8 +50,7 @@ impl Sys {
|
||||
Ok((username, uuid)) => (username, uuid),
|
||||
};
|
||||
|
||||
const INITIAL_VD: Option<u32> = Some(5); //will be changed after login
|
||||
let player = Player::new(username, None, INITIAL_VD, uuid);
|
||||
let player = Player::new(username, uuid);
|
||||
let is_admin = editable_settings.admins.contains(&uuid);
|
||||
|
||||
if !player.is_valid() {
|
||||
@ -60,7 +59,7 @@ impl Sys {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !client.registered && client.in_game.is_none() {
|
||||
if !players.contains(entity) {
|
||||
// Add Player component to this client
|
||||
let _ = players.insert(entity, player);
|
||||
player_metrics.players_connected.inc();
|
||||
@ -72,7 +71,6 @@ impl Sys {
|
||||
}
|
||||
|
||||
// Tell the client its request was successful.
|
||||
client.registered = true;
|
||||
register_stream.send(ServerRegisterAnswer::Ok(()))?;
|
||||
|
||||
// Send initial player list
|
||||
@ -83,6 +81,7 @@ impl Sys {
|
||||
// Add to list to notify all clients of the new player
|
||||
new_players.push(entity);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -182,10 +181,7 @@ impl<'a> System<'a> for Sys {
|
||||
for entity in new_players {
|
||||
if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) {
|
||||
let mut lazy_msg = None;
|
||||
for (_, general_stream) in (&mut clients, &mut general_streams)
|
||||
.join()
|
||||
.filter(|(c, _)| c.registered)
|
||||
{
|
||||
for (_, general_stream) in (&players, &mut general_streams).join() {
|
||||
if lazy_msg.is_none() {
|
||||
lazy_msg = Some(general_stream.prepare(&ServerGeneral::PlayerListUpdate(
|
||||
PlayerListUpdate::Add(*uid, PlayerInfo {
|
||||
|
@ -1,9 +1,11 @@
|
||||
use crate::{
|
||||
persistence::character_updater,
|
||||
presence::Presence,
|
||||
sys::{SysScheduler, SysTimer},
|
||||
};
|
||||
use common::{
|
||||
comp::{Inventory, Loadout, Player, Stats},
|
||||
comp::{Inventory, Loadout, Stats},
|
||||
msg::PresenceKind,
|
||||
span,
|
||||
};
|
||||
use specs::{Join, ReadExpect, ReadStorage, System, Write};
|
||||
@ -13,7 +15,7 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)] // TODO: Pending review in #587
|
||||
type SystemData = (
|
||||
ReadStorage<'a, Player>,
|
||||
ReadStorage<'a, Presence>,
|
||||
ReadStorage<'a, Stats>,
|
||||
ReadStorage<'a, Inventory>,
|
||||
ReadStorage<'a, Loadout>,
|
||||
@ -25,7 +27,7 @@ impl<'a> System<'a> for Sys {
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
players,
|
||||
presences,
|
||||
player_stats,
|
||||
player_inventories,
|
||||
player_loadouts,
|
||||
@ -39,17 +41,18 @@ impl<'a> System<'a> for Sys {
|
||||
timer.start();
|
||||
updater.batch_update(
|
||||
(
|
||||
&players,
|
||||
&presences,
|
||||
&player_stats,
|
||||
&player_inventories,
|
||||
&player_loadouts,
|
||||
)
|
||||
.join()
|
||||
.filter_map(|(player, stats, inventory, loadout)| {
|
||||
player
|
||||
.character_id
|
||||
.map(|id| (id, stats, inventory, loadout))
|
||||
}),
|
||||
.filter_map(
|
||||
|(presence, stats, inventory, loadout)| match presence.kind {
|
||||
PresenceKind::Character(id) => Some((id, stats, inventory, loadout)),
|
||||
PresenceKind::Spectator => None,
|
||||
},
|
||||
),
|
||||
);
|
||||
timer.end();
|
||||
}
|
||||
|
@ -3,11 +3,12 @@ use super::{
|
||||
SysTimer,
|
||||
};
|
||||
use crate::{
|
||||
client::{self, Client, RegionSubscription},
|
||||
client::Client,
|
||||
presence::{self, Presence, RegionSubscription},
|
||||
streams::{GeneralStream, GetStream},
|
||||
};
|
||||
use common::{
|
||||
comp::{Ori, Player, Pos, Vel},
|
||||
comp::{Ori, Pos, Vel},
|
||||
msg::ServerGeneral,
|
||||
region::{region_in_vd, regions_in_vd, Event as RegionEvent, RegionMap},
|
||||
span,
|
||||
@ -34,7 +35,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Vel>,
|
||||
ReadStorage<'a, Ori>,
|
||||
ReadStorage<'a, Player>,
|
||||
ReadStorage<'a, Presence>,
|
||||
ReadStorage<'a, Client>,
|
||||
WriteStorage<'a, GeneralStream>,
|
||||
WriteStorage<'a, RegionSubscription>,
|
||||
@ -53,8 +54,8 @@ impl<'a> System<'a> for Sys {
|
||||
positions,
|
||||
velocities,
|
||||
orientations,
|
||||
players,
|
||||
clients,
|
||||
presences,
|
||||
_clients,
|
||||
mut general_streams,
|
||||
mut subscriptions,
|
||||
mut deleted_entities,
|
||||
@ -76,23 +77,16 @@ impl<'a> System<'a> for Sys {
|
||||
// 7. Determine list of regions that are in range and iterate through it
|
||||
// - check if in hashset (hash calc) if not add it
|
||||
let mut regions_to_remove = Vec::new();
|
||||
for (_, subscription, pos, vd, client_entity, general_stream) in (
|
||||
&clients,
|
||||
for (subscription, pos, presence, client_entity, general_stream) in (
|
||||
&mut subscriptions,
|
||||
&positions,
|
||||
&players,
|
||||
&presences,
|
||||
&entities,
|
||||
&mut general_streams,
|
||||
)
|
||||
.join()
|
||||
.filter_map(|(client, s, pos, player, e, stream)| {
|
||||
if client.in_game.is_some() {
|
||||
player.view_distance.map(|v| (client, s, pos, v, e, stream))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
{
|
||||
let vd = presence.view_distance;
|
||||
// Calculate current chunk
|
||||
let chunk = (Vec2::<f32>::from(pos.0))
|
||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
||||
@ -107,7 +101,7 @@ impl<'a> System<'a> for Sys {
|
||||
})
|
||||
- Vec2::from(pos.0))
|
||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| {
|
||||
e.abs() > (sz / 2 + client::CHUNK_FUZZ) as f32
|
||||
e.abs() > (sz / 2 + presence::CHUNK_FUZZ) as f32
|
||||
})
|
||||
.reduce_or()
|
||||
{
|
||||
@ -123,7 +117,9 @@ impl<'a> System<'a> for Sys {
|
||||
*key,
|
||||
pos.0,
|
||||
(vd as f32 * chunk_size)
|
||||
+ (client::CHUNK_FUZZ as f32 + client::REGION_FUZZ as f32 + chunk_size)
|
||||
+ (presence::CHUNK_FUZZ as f32
|
||||
+ presence::REGION_FUZZ as f32
|
||||
+ chunk_size)
|
||||
* 2.0f32.sqrt(),
|
||||
) {
|
||||
// Add to the list of regions to remove
|
||||
@ -185,7 +181,7 @@ impl<'a> System<'a> for Sys {
|
||||
for key in regions_in_vd(
|
||||
pos.0,
|
||||
(vd as f32 * chunk_size)
|
||||
+ (client::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(),
|
||||
+ (presence::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(),
|
||||
) {
|
||||
// Send client initial info about the entities in this region if it was not
|
||||
// already within the set of subscribed regions
|
||||
@ -224,13 +220,9 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
/// Initialize region subscription
|
||||
pub fn initialize_region_subscription(world: &World, entity: specs::Entity) {
|
||||
if let (Some(client_pos), Some(client_vd), Some(general_stream)) = (
|
||||
if let (Some(client_pos), Some(presence), Some(general_stream)) = (
|
||||
world.read_storage::<Pos>().get(entity),
|
||||
world
|
||||
.read_storage::<Player>()
|
||||
.get(entity)
|
||||
.map(|pl| pl.view_distance)
|
||||
.and_then(|v| v),
|
||||
world.read_storage::<Presence>().get(entity),
|
||||
world.write_storage::<GeneralStream>().get_mut(entity),
|
||||
) {
|
||||
let fuzzy_chunk = (Vec2::<f32>::from(client_pos.0))
|
||||
@ -238,8 +230,8 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) {
|
||||
let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as f32;
|
||||
let regions = common::region::regions_in_vd(
|
||||
client_pos.0,
|
||||
(client_vd as f32 * chunk_size) as f32
|
||||
+ (client::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(),
|
||||
(presence.view_distance as f32 * chunk_size) as f32
|
||||
+ (presence::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(),
|
||||
);
|
||||
|
||||
let region_map = world.read_resource::<RegionMap>();
|
||||
|
@ -1,11 +1,12 @@
|
||||
use super::SysTimer;
|
||||
use crate::{
|
||||
chunk_generator::ChunkGenerator,
|
||||
presence::Presence,
|
||||
streams::{GetStream, InGameStream},
|
||||
Tick,
|
||||
};
|
||||
use common::{
|
||||
comp::{self, bird_medium, Alignment, Player, Pos},
|
||||
comp::{self, bird_medium, Alignment, Pos},
|
||||
event::{EventBus, ServerEvent},
|
||||
generation::get_npc_name,
|
||||
msg::ServerGeneral,
|
||||
@ -37,7 +38,7 @@ impl<'a> System<'a> for Sys {
|
||||
WriteExpect<'a, TerrainGrid>,
|
||||
Write<'a, TerrainChanges>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Player>,
|
||||
ReadStorage<'a, Presence>,
|
||||
WriteStorage<'a, InGameStream>,
|
||||
);
|
||||
|
||||
@ -51,7 +52,7 @@ impl<'a> System<'a> for Sys {
|
||||
mut terrain,
|
||||
mut terrain_changes,
|
||||
positions,
|
||||
players,
|
||||
presences,
|
||||
mut in_game_streams,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
@ -79,11 +80,8 @@ impl<'a> System<'a> for Sys {
|
||||
},
|
||||
};
|
||||
// Send the chunk to all nearby players.
|
||||
for (view_distance, pos, in_game_stream) in (&players, &positions, &mut in_game_streams)
|
||||
.join()
|
||||
.filter_map(|(player, pos, in_game_stream)| {
|
||||
player.view_distance.map(|vd| (vd, pos, in_game_stream))
|
||||
})
|
||||
for (presence, pos, in_game_stream) in
|
||||
(&presences, &positions, &mut in_game_streams).join()
|
||||
{
|
||||
let chunk_pos = terrain.pos_key(pos.0.map(|e| e as i32));
|
||||
// Subtract 2 from the offset before computing squared magnitude
|
||||
@ -93,7 +91,7 @@ impl<'a> System<'a> for Sys {
|
||||
.map(|e: i32| (e.abs() as u32).saturating_sub(2))
|
||||
.magnitude_squared();
|
||||
|
||||
if adjusted_dist_sqr <= view_distance.pow(2) {
|
||||
if adjusted_dist_sqr <= presence.view_distance.pow(2) {
|
||||
in_game_stream.send_fallible(ServerGeneral::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Ok(Box::new(chunk.clone())),
|
||||
@ -210,12 +208,8 @@ impl<'a> System<'a> for Sys {
|
||||
let mut should_drop = true;
|
||||
|
||||
// For each player with a position, calculate the distance.
|
||||
for (player, pos) in (&players, &positions).join() {
|
||||
if player
|
||||
.view_distance
|
||||
.map(|vd| chunk_in_vd(pos.0, chunk_key, &terrain, vd))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
for (presence, pos) in (&presences, &positions).join() {
|
||||
if chunk_in_vd(pos.0, chunk_key, &terrain, presence.view_distance) {
|
||||
should_drop = false;
|
||||
break;
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
use super::SysTimer;
|
||||
use crate::streams::{GetStream, InGameStream};
|
||||
use common::{
|
||||
comp::{Player, Pos},
|
||||
msg::ServerGeneral,
|
||||
span,
|
||||
state::TerrainChanges,
|
||||
terrain::TerrainGrid,
|
||||
use crate::{
|
||||
presence::Presence,
|
||||
streams::{GetStream, InGameStream},
|
||||
};
|
||||
use common::{comp::Pos, msg::ServerGeneral, span, state::TerrainChanges, terrain::TerrainGrid};
|
||||
use specs::{Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage};
|
||||
|
||||
/// This systems sends new chunks to clients as well as changes to existing
|
||||
@ -19,13 +16,13 @@ impl<'a> System<'a> for Sys {
|
||||
Read<'a, TerrainChanges>,
|
||||
Write<'a, SysTimer<Self>>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Player>,
|
||||
ReadStorage<'a, Presence>,
|
||||
WriteStorage<'a, InGameStream>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(terrain, terrain_changes, mut timer, positions, players, mut in_game_streams): Self::SystemData,
|
||||
(terrain, terrain_changes, mut timer, positions, presences, mut in_game_streams): Self::SystemData,
|
||||
) {
|
||||
span!(_guard, "run", "terrain_sync::Sys::run");
|
||||
timer.start();
|
||||
@ -34,12 +31,10 @@ impl<'a> System<'a> for Sys {
|
||||
'chunk: for chunk_key in &terrain_changes.modified_chunks {
|
||||
let mut lazy_msg = None;
|
||||
|
||||
for (player, pos, in_game_stream) in (&players, &positions, &mut in_game_streams).join()
|
||||
for (presence, pos, in_game_stream) in
|
||||
(&presences, &positions, &mut in_game_streams).join()
|
||||
{
|
||||
if player
|
||||
.view_distance
|
||||
.map(|vd| super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, vd))
|
||||
.unwrap_or(false)
|
||||
if super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, presence.view_distance)
|
||||
{
|
||||
if lazy_msg.is_none() {
|
||||
lazy_msg =
|
||||
@ -61,17 +56,15 @@ impl<'a> System<'a> for Sys {
|
||||
// TODO: Don't send all changed blocks to all clients
|
||||
// Sync changed blocks
|
||||
let mut lazy_msg = None;
|
||||
for (player, in_game_stream) in (&players, &mut in_game_streams).join() {
|
||||
for (_, in_game_stream) in (&presences, &mut in_game_streams).join() {
|
||||
if lazy_msg.is_none() {
|
||||
lazy_msg = Some(in_game_stream.prepare(&ServerGeneral::TerrainBlockUpdates(
|
||||
terrain_changes.modified_blocks.clone(),
|
||||
)));
|
||||
}
|
||||
if player.view_distance.is_some() {
|
||||
lazy_msg
|
||||
.as_ref()
|
||||
.map(|ref msg| in_game_stream.0.send_raw(&msg));
|
||||
}
|
||||
lazy_msg
|
||||
.as_ref()
|
||||
.map(|ref msg| in_game_stream.0.send_raw(&msg));
|
||||
}
|
||||
|
||||
timer.end();
|
||||
|
@ -64,6 +64,7 @@ use common::{
|
||||
item::{ItemDesc, Quality},
|
||||
BuffKind,
|
||||
},
|
||||
msg::PresenceKind,
|
||||
span,
|
||||
sync::Uid,
|
||||
terrain::TerrainChunk,
|
||||
@ -658,7 +659,12 @@ impl Hud {
|
||||
let server = &client.server_info.name;
|
||||
// Get the id, unwrap is safe because this CANNOT be None at this
|
||||
// point.
|
||||
let character_id = client.active_character_id.unwrap();
|
||||
|
||||
let character_id = match client.presence().unwrap() {
|
||||
PresenceKind::Character(id) => id,
|
||||
PresenceKind::Spectator => unreachable!("HUD creation in Spectator mode!"),
|
||||
};
|
||||
|
||||
// Create a new HotbarState from the persisted slots.
|
||||
let hotbar_state =
|
||||
HotbarState::new(global_state.profile.get_hotbar_slots(server, character_id));
|
||||
|
@ -61,11 +61,11 @@ 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_in_game, client_registered) = {
|
||||
let (client_presence, client_registered) = {
|
||||
let client = self.client.borrow();
|
||||
(client.in_game(), client.registered())
|
||||
(client.presence(), client.registered())
|
||||
};
|
||||
if client_in_game.is_none() && client_registered {
|
||||
if client_presence.is_none() && client_registered {
|
||||
// Handle window events
|
||||
for event in events {
|
||||
if self.char_selection_ui.handle_event(event.clone()) {
|
||||
|
@ -18,6 +18,7 @@ use common::{
|
||||
comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel},
|
||||
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
||||
event::EventBus,
|
||||
msg::PresenceKind,
|
||||
outcome::Outcome,
|
||||
span,
|
||||
terrain::{Block, BlockKind},
|
||||
@ -211,11 +212,11 @@ impl PlayState for SessionState {
|
||||
));
|
||||
|
||||
// TODO: can this be a method on the session or are there borrowcheck issues?
|
||||
let (client_in_game, client_registered) = {
|
||||
let (client_presence, client_registered) = {
|
||||
let client = self.client.borrow();
|
||||
(client.in_game(), client.registered())
|
||||
(client.presence(), client.registered())
|
||||
};
|
||||
if client_in_game.is_some() {
|
||||
if client_presence.is_some() {
|
||||
// Update MyEntity
|
||||
// Note: Alternatively, the client could emit an event when the entity changes
|
||||
// which may or may not be more elegant
|
||||
@ -927,7 +928,12 @@ impl PlayState for SessionState {
|
||||
|
||||
let server = &client.server_info.name;
|
||||
// If we are changing the hotbar state this CANNOT be None.
|
||||
let character_id = client.active_character_id.unwrap();
|
||||
let character_id = match client.presence().unwrap() {
|
||||
PresenceKind::Character(id) => id,
|
||||
PresenceKind::Spectator => {
|
||||
unreachable!("HUD adaption in Spectator mode!")
|
||||
},
|
||||
};
|
||||
|
||||
// Get or update the ServerProfile.
|
||||
global_state
|
||||
@ -1080,7 +1086,7 @@ impl PlayState for SessionState {
|
||||
self.cleanup();
|
||||
|
||||
PlayStateResult::Continue
|
||||
} else if client_registered && client_in_game.is_none() {
|
||||
} else if client_registered && client_presence.is_none() {
|
||||
PlayStateResult::Switch(Box::new(CharSelectionState::new(
|
||||
global_state,
|
||||
Rc::clone(&self.client),
|
||||
|
Loading…
Reference in New Issue
Block a user