mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
create a ServerMsg and ClientMsg enum and verify the state when in debug mode to benefit from the transition
This commit is contained in:
parent
e8452fafc6
commit
ff374eab59
@ -25,11 +25,11 @@ use common::{
|
|||||||
},
|
},
|
||||||
event::{EventBus, LocalEvent},
|
event::{EventBus, LocalEvent},
|
||||||
msg::{
|
msg::{
|
||||||
validate_chat_msg, ChatMsgValidationError, ClientCharacterScreenMsg, ClientGeneralMsg,
|
validate_chat_msg, ChatMsgValidationError, ClientCharacterScreen, ClientGeneral,
|
||||||
ClientInGameMsg, ClientIngame, ClientRegisterMsg, ClientType, DisconnectReason,
|
ClientInGame, ClientIngame, ClientMsg, ClientRegister, ClientType, DisconnectReason,
|
||||||
InviteAnswer, Notification, PingMsg, PlayerInfo, PlayerListUpdate, RegisterError,
|
InviteAnswer, Notification, PingMsg, PlayerInfo, PlayerListUpdate, RegisterError,
|
||||||
ServerCharacterScreenMsg, ServerGeneralMsg, ServerInGameMsg, ServerInfo, ServerInitMsg,
|
ServerCharacterScreen, ServerGeneral, ServerInGame, ServerInfo, ServerInit,
|
||||||
ServerRegisterAnswerMsg, MAX_BYTES_CHAT_MSG,
|
ServerRegisterAnswer, MAX_BYTES_CHAT_MSG,
|
||||||
},
|
},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
recipe::RecipeBook,
|
recipe::RecipeBook,
|
||||||
@ -71,7 +71,7 @@ pub enum Event {
|
|||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
registered: bool,
|
registered: bool,
|
||||||
client_ingame: Option<ClientIngame>,
|
in_game: Option<ClientIngame>,
|
||||||
thread_pool: ThreadPool,
|
thread_pool: ThreadPool,
|
||||||
pub server_info: ServerInfo,
|
pub server_info: ServerInfo,
|
||||||
/// Just the "base" layer for LOD; currently includes colors and nothing
|
/// Just the "base" layer for LOD; currently includes colors and nothing
|
||||||
@ -193,7 +193,7 @@ impl Client {
|
|||||||
max_group_size,
|
max_group_size,
|
||||||
client_timeout,
|
client_timeout,
|
||||||
) = match block_on(register_stream.recv())? {
|
) = match block_on(register_stream.recv())? {
|
||||||
ServerInitMsg::GameSync {
|
ServerInit::GameSync {
|
||||||
entity_package,
|
entity_package,
|
||||||
time_of_day,
|
time_of_day,
|
||||||
max_group_size,
|
max_group_size,
|
||||||
@ -362,7 +362,7 @@ impl Client {
|
|||||||
client_timeout,
|
client_timeout,
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
ServerInitMsg::TooManyPlayers => Err(Error::TooManyPlayers),
|
ServerInit::TooManyPlayers => Err(Error::TooManyPlayers),
|
||||||
}?;
|
}?;
|
||||||
ping_stream.send(PingMsg::Ping)?;
|
ping_stream.send(PingMsg::Ping)?;
|
||||||
|
|
||||||
@ -376,7 +376,7 @@ impl Client {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
registered: false,
|
registered: false,
|
||||||
client_ingame: None,
|
in_game: None,
|
||||||
thread_pool,
|
thread_pool,
|
||||||
server_info,
|
server_info,
|
||||||
world_map,
|
world_map,
|
||||||
@ -444,10 +444,9 @@ impl Client {
|
|||||||
}
|
}
|
||||||
).unwrap_or(Ok(username))?;
|
).unwrap_or(Ok(username))?;
|
||||||
|
|
||||||
self.register_stream
|
self.send_msg_err(ClientRegister { token_or_username })?;
|
||||||
.send(ClientRegisterMsg { token_or_username })?;
|
|
||||||
|
|
||||||
match block_on(self.register_stream.recv::<ServerRegisterAnswerMsg>())? {
|
match block_on(self.register_stream.recv::<ServerRegisterAnswer>())? {
|
||||||
Err(RegisterError::AlreadyLoggedIn) => Err(Error::AlreadyLoggedIn),
|
Err(RegisterError::AlreadyLoggedIn) => Err(Error::AlreadyLoggedIn),
|
||||||
Err(RegisterError::AuthError(err)) => Err(Error::AuthErr(err)),
|
Err(RegisterError::AuthError(err)) => Err(Error::AuthErr(err)),
|
||||||
Err(RegisterError::InvalidCharacter) => Err(Error::InvalidCharacter),
|
Err(RegisterError::InvalidCharacter) => Err(Error::InvalidCharacter),
|
||||||
@ -460,14 +459,69 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_msg_err<S>(&mut self, msg: S) -> Result<(), network::StreamError>
|
||||||
|
where
|
||||||
|
S: Into<ClientMsg>,
|
||||||
|
{
|
||||||
|
let msg: ClientMsg = msg.into();
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
//There assertions veriy that the state is correct when a msg is send!
|
||||||
|
match &msg {
|
||||||
|
ClientMsg::Type(_) | ClientMsg::Register(_) => assert!(
|
||||||
|
!self.registered,
|
||||||
|
"must not send msg when already registered"
|
||||||
|
),
|
||||||
|
ClientMsg::CharacterScreen(_) => {
|
||||||
|
assert!(
|
||||||
|
self.registered,
|
||||||
|
"must not send character_screen msg when not registered"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
self.in_game.is_none(),
|
||||||
|
"must not send character_screen msg when not in character screen"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
ClientMsg::InGame(_) => assert!(
|
||||||
|
self.in_game.is_some(),
|
||||||
|
"must not send in_game msg when not in game"
|
||||||
|
),
|
||||||
|
ClientMsg::General(_) => assert!(
|
||||||
|
self.registered,
|
||||||
|
"must not send general msg when not registered"
|
||||||
|
),
|
||||||
|
ClientMsg::Ping(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match msg {
|
||||||
|
ClientMsg::Type(msg) => self.register_stream.send(msg),
|
||||||
|
ClientMsg::Register(msg) => self.register_stream.send(msg),
|
||||||
|
ClientMsg::CharacterScreen(msg) => self.character_screen_stream.send(msg),
|
||||||
|
ClientMsg::InGame(msg) => self.in_game_stream.send(msg),
|
||||||
|
ClientMsg::General(msg) => self.general_stream.send(msg),
|
||||||
|
ClientMsg::Ping(msg) => self.ping_stream.send(msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_msg<S>(&mut self, msg: S)
|
||||||
|
where
|
||||||
|
S: Into<ClientMsg>,
|
||||||
|
{
|
||||||
|
let res = self.send_msg_err(msg);
|
||||||
|
if let Err(e) = res {
|
||||||
|
warn!(
|
||||||
|
?e,
|
||||||
|
"connection to server no longer possible, couldn't send msg"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Request a state transition to `ClientState::Character`.
|
/// Request a state transition to `ClientState::Character`.
|
||||||
pub fn request_character(&mut self, character_id: CharacterId) {
|
pub fn request_character(&mut self, character_id: CharacterId) {
|
||||||
self.character_screen_stream
|
self.send_msg(ClientCharacterScreen::Character(character_id));
|
||||||
.send(ClientCharacterScreenMsg::Character(character_id))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
//Assume we are in_game unless server tells us otherwise
|
//Assume we are in_game unless server tells us otherwise
|
||||||
self.client_ingame = Some(ClientIngame::Character);
|
self.in_game = Some(ClientIngame::Character);
|
||||||
|
|
||||||
self.active_character_id = Some(character_id);
|
self.active_character_id = Some(character_id);
|
||||||
}
|
}
|
||||||
@ -475,87 +529,59 @@ impl Client {
|
|||||||
/// Load the current players character list
|
/// Load the current players character list
|
||||||
pub fn load_character_list(&mut self) {
|
pub fn load_character_list(&mut self) {
|
||||||
self.character_list.loading = true;
|
self.character_list.loading = true;
|
||||||
self.character_screen_stream
|
self.send_msg(ClientCharacterScreen::RequestCharacterList);
|
||||||
.send(ClientCharacterScreenMsg::RequestCharacterList)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// New character creation
|
/// New character creation
|
||||||
pub fn create_character(&mut self, alias: String, tool: Option<String>, body: comp::Body) {
|
pub fn create_character(&mut self, alias: String, tool: Option<String>, body: comp::Body) {
|
||||||
self.character_list.loading = true;
|
self.character_list.loading = true;
|
||||||
self.character_screen_stream
|
self.send_msg(ClientCharacterScreen::CreateCharacter { alias, tool, body });
|
||||||
.send(ClientCharacterScreenMsg::CreateCharacter { alias, tool, body })
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Character deletion
|
/// Character deletion
|
||||||
pub fn delete_character(&mut self, character_id: CharacterId) {
|
pub fn delete_character(&mut self, character_id: CharacterId) {
|
||||||
self.character_list.loading = true;
|
self.character_list.loading = true;
|
||||||
self.character_screen_stream
|
self.send_msg(ClientCharacterScreen::DeleteCharacter(character_id));
|
||||||
.send(ClientCharacterScreenMsg::DeleteCharacter(character_id))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send disconnect message to the server
|
/// Send disconnect message to the server
|
||||||
pub fn request_logout(&mut self) {
|
pub fn request_logout(&mut self) {
|
||||||
debug!("Requesting logout from server");
|
debug!("Requesting logout from server");
|
||||||
if let Err(e) = self.general_stream.send(ClientGeneralMsg::Disconnect) {
|
self.send_msg(ClientGeneral::Disconnect);
|
||||||
error!(
|
|
||||||
?e,
|
|
||||||
"Couldn't send disconnect package to server, did server close already?"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request a state transition to `ClientState::Registered` from an ingame
|
/// Request a state transition to `ClientState::Registered` from an ingame
|
||||||
/// state.
|
/// state.
|
||||||
pub fn request_remove_character(&mut self) {
|
pub fn request_remove_character(&mut self) { self.send_msg(ClientInGame::ExitInGame); }
|
||||||
self.in_game_stream
|
|
||||||
.send(ClientInGameMsg::ExitInGame)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_view_distance(&mut self, view_distance: u32) {
|
pub fn set_view_distance(&mut self, view_distance: u32) {
|
||||||
self.view_distance = Some(view_distance.max(1).min(65));
|
self.view_distance = Some(view_distance.max(1).min(65));
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::SetViewDistance(self.view_distance.unwrap()));
|
||||||
.send(ClientInGameMsg::SetViewDistance(
|
|
||||||
self.view_distance.unwrap(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
// Can't fail
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_slot(&mut self, slot: comp::slot::Slot) {
|
pub fn use_slot(&mut self, slot: comp::slot::Slot) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::InventoryManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::InventoryManip(
|
|
||||||
InventoryManip::Use(slot),
|
InventoryManip::Use(slot),
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn swap_slots(&mut self, a: comp::slot::Slot, b: comp::slot::Slot) {
|
pub fn swap_slots(&mut self, a: comp::slot::Slot, b: comp::slot::Slot) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::InventoryManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::InventoryManip(
|
|
||||||
InventoryManip::Swap(a, b),
|
InventoryManip::Swap(a, b),
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drop_slot(&mut self, slot: comp::slot::Slot) {
|
pub fn drop_slot(&mut self, slot: comp::slot::Slot) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::InventoryManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::InventoryManip(
|
|
||||||
InventoryManip::Drop(slot),
|
InventoryManip::Drop(slot),
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pick_up(&mut self, entity: EcsEntity) {
|
pub fn pick_up(&mut self, entity: EcsEntity) {
|
||||||
if let Some(uid) = self.state.read_component_copied(entity) {
|
if let Some(uid) = self.state.read_component_copied(entity) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::InventoryManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::InventoryManip(
|
|
||||||
InventoryManip::Pickup(uid),
|
InventoryManip::Pickup(uid),
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,11 +599,9 @@ impl Client {
|
|||||||
|
|
||||||
pub fn craft_recipe(&mut self, recipe: &str) -> bool {
|
pub fn craft_recipe(&mut self, recipe: &str) -> bool {
|
||||||
if self.can_craft_recipe(recipe) {
|
if self.can_craft_recipe(recipe) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::InventoryManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::InventoryManip(
|
|
||||||
InventoryManip::CraftRecipe(recipe.to_string()),
|
InventoryManip::CraftRecipe(recipe.to_string()),
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -594,15 +618,11 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable_lantern(&mut self) {
|
pub fn enable_lantern(&mut self) {
|
||||||
self.singleton_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::EnableLantern));
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::EnableLantern))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_lantern(&mut self) {
|
pub fn disable_lantern(&mut self) {
|
||||||
self.singleton_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::DisableLantern));
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::DisableLantern))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max_group_size(&self) -> u32 { self.max_group_size }
|
pub fn max_group_size(&self) -> u32 { self.max_group_size }
|
||||||
@ -620,55 +640,43 @@ impl Client {
|
|||||||
pub fn pending_invites(&self) -> &HashSet<Uid> { &self.pending_invites }
|
pub fn pending_invites(&self) -> &HashSet<Uid> { &self.pending_invites }
|
||||||
|
|
||||||
pub fn send_group_invite(&mut self, invitee: Uid) {
|
pub fn send_group_invite(&mut self, invitee: Uid) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::GroupManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::GroupManip(
|
|
||||||
GroupManip::Invite(invitee),
|
GroupManip::Invite(invitee),
|
||||||
)))
|
)))
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn accept_group_invite(&mut self) {
|
pub fn accept_group_invite(&mut self) {
|
||||||
// Clear invite
|
// Clear invite
|
||||||
self.group_invite.take();
|
self.group_invite.take();
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::GroupManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::GroupManip(
|
|
||||||
GroupManip::Accept,
|
GroupManip::Accept,
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decline_group_invite(&mut self) {
|
pub fn decline_group_invite(&mut self) {
|
||||||
// Clear invite
|
// Clear invite
|
||||||
self.group_invite.take();
|
self.group_invite.take();
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::GroupManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::GroupManip(
|
|
||||||
GroupManip::Decline,
|
GroupManip::Decline,
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn leave_group(&mut self) {
|
pub fn leave_group(&mut self) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::GroupManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::GroupManip(
|
|
||||||
GroupManip::Leave,
|
GroupManip::Leave,
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kick_from_group(&mut self, uid: Uid) {
|
pub fn kick_from_group(&mut self, uid: Uid) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::GroupManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::GroupManip(
|
|
||||||
GroupManip::Kick(uid),
|
GroupManip::Kick(uid),
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assign_group_leader(&mut self, uid: Uid) {
|
pub fn assign_group_leader(&mut self, uid: Uid) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::GroupManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::GroupManip(
|
|
||||||
GroupManip::AssignLeader(uid),
|
GroupManip::AssignLeader(uid),
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_mounted(&self) -> bool {
|
pub fn is_mounted(&self) -> bool {
|
||||||
@ -689,17 +697,11 @@ impl Client {
|
|||||||
|
|
||||||
pub fn mount(&mut self, entity: EcsEntity) {
|
pub fn mount(&mut self, entity: EcsEntity) {
|
||||||
if let Some(uid) = self.state.read_component_copied(entity) {
|
if let Some(uid) = self.state.read_component_copied(entity) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::Mount(uid)));
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::Mount(uid)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unmount(&mut self) {
|
pub fn unmount(&mut self) { self.send_msg(ClientInGame::ControlEvent(ControlEvent::Unmount)); }
|
||||||
self.in_game_stream
|
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::Unmount))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn respawn(&mut self) {
|
pub fn respawn(&mut self) {
|
||||||
if self
|
if self
|
||||||
@ -709,9 +711,7 @@ impl Client {
|
|||||||
.get(self.entity)
|
.get(self.entity)
|
||||||
.map_or(false, |s| s.is_dead)
|
.map_or(false, |s| s.is_dead)
|
||||||
{
|
{
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::Respawn));
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::Respawn))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -808,9 +808,7 @@ impl Client {
|
|||||||
{
|
{
|
||||||
controller.actions.push(control_action);
|
controller.actions.push(control_action);
|
||||||
}
|
}
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlAction(control_action));
|
||||||
.send(ClientInGameMsg::ControlAction(control_action))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_distance(&self) -> Option<u32> { self.view_distance }
|
pub fn view_distance(&self) -> Option<u32> { self.view_distance }
|
||||||
@ -839,10 +837,7 @@ impl Client {
|
|||||||
/// Send a chat message to the server.
|
/// Send a chat message to the server.
|
||||||
pub fn send_chat(&mut self, message: String) {
|
pub fn send_chat(&mut self, message: String) {
|
||||||
match validate_chat_msg(&message) {
|
match validate_chat_msg(&message) {
|
||||||
Ok(()) => self
|
Ok(()) => self.send_msg(ClientGeneral::ChatMsg(message)),
|
||||||
.general_stream
|
|
||||||
.send(ClientGeneralMsg::ChatMsg(message))
|
|
||||||
.unwrap(),
|
|
||||||
Err(ChatMsgValidationError::TooLong) => tracing::warn!(
|
Err(ChatMsgValidationError::TooLong) => tracing::warn!(
|
||||||
"Attempted to send a message that's too long (Over {} bytes)",
|
"Attempted to send a message that's too long (Over {} bytes)",
|
||||||
MAX_BYTES_CHAT_MSG
|
MAX_BYTES_CHAT_MSG
|
||||||
@ -857,23 +852,15 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn place_block(&mut self, pos: Vec3<i32>, block: Block) {
|
pub fn place_block(&mut self, pos: Vec3<i32>, block: Block) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::PlaceBlock(pos, block));
|
||||||
.send(ClientInGameMsg::PlaceBlock(pos, block))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_block(&mut self, pos: Vec3<i32>) {
|
pub fn remove_block(&mut self, pos: Vec3<i32>) { self.send_msg(ClientInGame::BreakBlock(pos)); }
|
||||||
self.in_game_stream
|
|
||||||
.send(ClientInGameMsg::BreakBlock(pos))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn collect_block(&mut self, pos: Vec3<i32>) {
|
pub fn collect_block(&mut self, pos: Vec3<i32>) {
|
||||||
self.in_game_stream
|
self.send_msg(ClientInGame::ControlEvent(ControlEvent::InventoryManip(
|
||||||
.send(ClientInGameMsg::ControlEvent(ControlEvent::InventoryManip(
|
|
||||||
InventoryManip::Collect(pos),
|
InventoryManip::Collect(pos),
|
||||||
)))
|
)));
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a single client tick, handle input and update the game state by
|
/// Execute a single client tick, handle input and update the game state by
|
||||||
@ -905,7 +892,7 @@ impl Client {
|
|||||||
|
|
||||||
// 1) Handle input from frontend.
|
// 1) Handle input from frontend.
|
||||||
// Pass character actions from frontend input to the player's entity.
|
// Pass character actions from frontend input to the player's entity.
|
||||||
if self.client_ingame.is_some() {
|
if self.in_game.is_some() {
|
||||||
if let Err(e) = self
|
if let Err(e) = self
|
||||||
.state
|
.state
|
||||||
.ecs()
|
.ecs()
|
||||||
@ -928,8 +915,7 @@ impl Client {
|
|||||||
"Couldn't access controller component on client entity"
|
"Couldn't access controller component on client entity"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.in_game_stream
|
self.send_msg_err(ClientInGame::ControllerInputs(inputs))?;
|
||||||
.send(ClientInGameMsg::ControllerInputs(inputs))?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Build up a list of events for this frame, to be passed to the frontend.
|
// 2) Build up a list of events for this frame, to be passed to the frontend.
|
||||||
@ -1033,8 +1019,9 @@ impl Client {
|
|||||||
if self.state.terrain().get_key(*key).is_none() {
|
if self.state.terrain().get_key(*key).is_none() {
|
||||||
if !skip_mode && !self.pending_chunks.contains_key(key) {
|
if !skip_mode && !self.pending_chunks.contains_key(key) {
|
||||||
if self.pending_chunks.len() < 4 {
|
if self.pending_chunks.len() < 4 {
|
||||||
self.in_game_stream
|
self.send_msg_err(ClientInGame::TerrainChunkRequest {
|
||||||
.send(ClientInGameMsg::TerrainChunkRequest { key: *key })?;
|
key: *key,
|
||||||
|
})?;
|
||||||
self.pending_chunks.insert(*key, Instant::now());
|
self.pending_chunks.insert(*key, Instant::now());
|
||||||
} else {
|
} else {
|
||||||
skip_mode = true;
|
skip_mode = true;
|
||||||
@ -1066,19 +1053,19 @@ impl Client {
|
|||||||
|
|
||||||
// Send a ping to the server once every second
|
// Send a ping to the server once every second
|
||||||
if self.state.get_time() - self.last_server_ping > 1. {
|
if self.state.get_time() - self.last_server_ping > 1. {
|
||||||
self.ping_stream.send(PingMsg::Ping)?;
|
self.send_msg_err(PingMsg::Ping)?;
|
||||||
self.last_server_ping = self.state.get_time();
|
self.last_server_ping = self.state.get_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6) Update the server about the player's physics attributes.
|
// 6) Update the server about the player's physics attributes.
|
||||||
if self.client_ingame.is_some() {
|
if self.in_game.is_some() {
|
||||||
if let (Some(pos), Some(vel), Some(ori)) = (
|
if let (Some(pos), Some(vel), Some(ori)) = (
|
||||||
self.state.read_storage().get(self.entity).cloned(),
|
self.state.read_storage().get(self.entity).cloned(),
|
||||||
self.state.read_storage().get(self.entity).cloned(),
|
self.state.read_storage().get(self.entity).cloned(),
|
||||||
self.state.read_storage().get(self.entity).cloned(),
|
self.state.read_storage().get(self.entity).cloned(),
|
||||||
) {
|
) {
|
||||||
self.in_game_stream
|
self.in_game_stream
|
||||||
.send(ClientInGameMsg::PlayerPhysics { pos, vel, ori })?;
|
.send(ClientInGame::PlayerPhysics { pos, vel, ori })?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1108,26 +1095,26 @@ impl Client {
|
|||||||
fn handle_server_msg(
|
fn handle_server_msg(
|
||||||
&mut self,
|
&mut self,
|
||||||
frontend_events: &mut Vec<Event>,
|
frontend_events: &mut Vec<Event>,
|
||||||
msg: ServerGeneralMsg,
|
msg: ServerGeneral,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
match msg {
|
match msg {
|
||||||
ServerGeneralMsg::Disconnect(reason) => match reason {
|
ServerGeneral::Disconnect(reason) => match reason {
|
||||||
DisconnectReason::Shutdown => return Err(Error::ServerShutdown),
|
DisconnectReason::Shutdown => return Err(Error::ServerShutdown),
|
||||||
DisconnectReason::Requested => {
|
DisconnectReason::Requested => {
|
||||||
debug!("finally sending ClientMsg::Terminate");
|
debug!("finally sending ClientMsg::Terminate");
|
||||||
frontend_events.push(Event::Disconnect);
|
frontend_events.push(Event::Disconnect);
|
||||||
self.general_stream.send(ClientGeneralMsg::Terminate)?;
|
self.send_msg_err(ClientGeneral::Terminate)?;
|
||||||
},
|
},
|
||||||
DisconnectReason::Kicked(reason) => {
|
DisconnectReason::Kicked(reason) => {
|
||||||
debug!("sending ClientMsg::Terminate because we got kicked");
|
debug!("sending ClientMsg::Terminate because we got kicked");
|
||||||
frontend_events.push(Event::Kicked(reason));
|
frontend_events.push(Event::Kicked(reason));
|
||||||
self.general_stream.send(ClientGeneralMsg::Terminate)?;
|
self.send_msg_err(ClientGeneral::Terminate)?;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::Init(list)) => {
|
ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init(list)) => {
|
||||||
self.player_list = list
|
self.player_list = list
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::Add(uid, player_info)) => {
|
ServerGeneral::PlayerListUpdate(PlayerListUpdate::Add(uid, player_info)) => {
|
||||||
if let Some(old_player_info) = self.player_list.insert(uid, player_info.clone()) {
|
if let Some(old_player_info) = self.player_list.insert(uid, player_info.clone()) {
|
||||||
warn!(
|
warn!(
|
||||||
"Received msg to insert {} with uid {} into the player list but there was \
|
"Received msg to insert {} with uid {} into the player list but there was \
|
||||||
@ -1136,7 +1123,7 @@ impl Client {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::Admin(uid, admin)) => {
|
ServerGeneral::PlayerListUpdate(PlayerListUpdate::Admin(uid, admin)) => {
|
||||||
if let Some(player_info) = self.player_list.get_mut(&uid) {
|
if let Some(player_info) = self.player_list.get_mut(&uid) {
|
||||||
player_info.is_admin = admin;
|
player_info.is_admin = admin;
|
||||||
} else {
|
} else {
|
||||||
@ -1147,7 +1134,7 @@ impl Client {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::SelectedCharacter(
|
ServerGeneral::PlayerListUpdate(PlayerListUpdate::SelectedCharacter(
|
||||||
uid,
|
uid,
|
||||||
char_info,
|
char_info,
|
||||||
)) => {
|
)) => {
|
||||||
@ -1161,7 +1148,7 @@ impl Client {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::LevelChange(uid, next_level)) => {
|
ServerGeneral::PlayerListUpdate(PlayerListUpdate::LevelChange(uid, next_level)) => {
|
||||||
if let Some(player_info) = self.player_list.get_mut(&uid) {
|
if let Some(player_info) = self.player_list.get_mut(&uid) {
|
||||||
player_info.character = match &player_info.character {
|
player_info.character = match &player_info.character {
|
||||||
Some(character) => Some(common::msg::CharacterInfo {
|
Some(character) => Some(common::msg::CharacterInfo {
|
||||||
@ -1180,7 +1167,7 @@ impl Client {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::Remove(uid)) => {
|
ServerGeneral::PlayerListUpdate(PlayerListUpdate::Remove(uid)) => {
|
||||||
// Instead of removing players, mark them as offline because we need to
|
// Instead of removing players, mark them as offline because we need to
|
||||||
// remember the names of disconnected players in chat.
|
// remember the names of disconnected players in chat.
|
||||||
//
|
//
|
||||||
@ -1205,7 +1192,7 @@ impl Client {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::Alias(uid, new_name)) => {
|
ServerGeneral::PlayerListUpdate(PlayerListUpdate::Alias(uid, new_name)) => {
|
||||||
if let Some(player_info) = self.player_list.get_mut(&uid) {
|
if let Some(player_info) = self.player_list.get_mut(&uid) {
|
||||||
player_info.player_alias = new_name;
|
player_info.player_alias = new_name;
|
||||||
} else {
|
} else {
|
||||||
@ -1216,38 +1203,38 @@ impl Client {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::ChatMsg(m) => frontend_events.push(Event::Chat(m)),
|
ServerGeneral::ChatMsg(m) => frontend_events.push(Event::Chat(m)),
|
||||||
ServerGeneralMsg::SetPlayerEntity(uid) => {
|
ServerGeneral::SetPlayerEntity(uid) => {
|
||||||
if let Some(entity) = self.state.ecs().entity_from_uid(uid.0) {
|
if let Some(entity) = self.state.ecs().entity_from_uid(uid.0) {
|
||||||
self.entity = entity;
|
self.entity = entity;
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::Other("Failed to find entity from uid.".to_owned()));
|
return Err(Error::Other("Failed to find entity from uid.".to_owned()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::TimeOfDay(time_of_day) => {
|
ServerGeneral::TimeOfDay(time_of_day) => {
|
||||||
*self.state.ecs_mut().write_resource() = time_of_day;
|
*self.state.ecs_mut().write_resource() = time_of_day;
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::EntitySync(entity_sync_package) => {
|
ServerGeneral::EntitySync(entity_sync_package) => {
|
||||||
self.state
|
self.state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.apply_entity_sync_package(entity_sync_package);
|
.apply_entity_sync_package(entity_sync_package);
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::CompSync(comp_sync_package) => {
|
ServerGeneral::CompSync(comp_sync_package) => {
|
||||||
self.state
|
self.state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.apply_comp_sync_package(comp_sync_package);
|
.apply_comp_sync_package(comp_sync_package);
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::CreateEntity(entity_package) => {
|
ServerGeneral::CreateEntity(entity_package) => {
|
||||||
self.state.ecs_mut().apply_entity_package(entity_package);
|
self.state.ecs_mut().apply_entity_package(entity_package);
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::DeleteEntity(entity) => {
|
ServerGeneral::DeleteEntity(entity) => {
|
||||||
if self.uid() != Some(entity) {
|
if self.uid() != Some(entity) {
|
||||||
self.state
|
self.state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.delete_entity_and_clear_from_uid_allocator(entity.0);
|
.delete_entity_and_clear_from_uid_allocator(entity.0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerGeneralMsg::Notification(n) => {
|
ServerGeneral::Notification(n) => {
|
||||||
frontend_events.push(Event::Notification(n));
|
frontend_events.push(Event::Notification(n));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1257,10 +1244,10 @@ impl Client {
|
|||||||
fn handle_server_in_game_msg(
|
fn handle_server_in_game_msg(
|
||||||
&mut self,
|
&mut self,
|
||||||
frontend_events: &mut Vec<Event>,
|
frontend_events: &mut Vec<Event>,
|
||||||
msg: ServerInGameMsg,
|
msg: ServerInGame,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
match msg {
|
match msg {
|
||||||
ServerInGameMsg::GroupUpdate(change_notification) => {
|
ServerInGame::GroupUpdate(change_notification) => {
|
||||||
use comp::group::ChangeNotification::*;
|
use comp::group::ChangeNotification::*;
|
||||||
// Note: we use a hashmap since this would not work with entities outside
|
// Note: we use a hashmap since this would not work with entities outside
|
||||||
// the view distance
|
// the view distance
|
||||||
@ -1332,15 +1319,15 @@ impl Client {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerInGameMsg::GroupInvite { inviter, timeout } => {
|
ServerInGame::GroupInvite { inviter, timeout } => {
|
||||||
self.group_invite = Some((inviter, std::time::Instant::now(), timeout));
|
self.group_invite = Some((inviter, std::time::Instant::now(), timeout));
|
||||||
},
|
},
|
||||||
ServerInGameMsg::InvitePending(uid) => {
|
ServerInGame::InvitePending(uid) => {
|
||||||
if !self.pending_invites.insert(uid) {
|
if !self.pending_invites.insert(uid) {
|
||||||
warn!("Received message about pending invite that was already pending");
|
warn!("Received message about pending invite that was already pending");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ServerInGameMsg::InviteComplete { target, answer } => {
|
ServerInGame::InviteComplete { target, answer } => {
|
||||||
if !self.pending_invites.remove(&target) {
|
if !self.pending_invites.remove(&target) {
|
||||||
warn!(
|
warn!(
|
||||||
"Received completed invite message for invite that was not in the list of \
|
"Received completed invite message for invite that was not in the list of \
|
||||||
@ -1358,11 +1345,11 @@ impl Client {
|
|||||||
frontend_events.push(Event::Chat(comp::ChatType::Meta.chat_msg(msg)));
|
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 `in_game = None`
|
||||||
ServerInGameMsg::ExitInGameSuccess => {
|
ServerInGame::ExitInGameSuccess => {
|
||||||
self.client_ingame = None;
|
self.in_game = None;
|
||||||
self.clean_state();
|
self.clean_state();
|
||||||
},
|
},
|
||||||
ServerInGameMsg::InventoryUpdate(mut inventory, event) => {
|
ServerInGame::InventoryUpdate(mut inventory, event) => {
|
||||||
match event {
|
match event {
|
||||||
InventoryUpdateEvent::CollectFailed => {},
|
InventoryUpdateEvent::CollectFailed => {},
|
||||||
_ => {
|
_ => {
|
||||||
@ -1376,25 +1363,25 @@ impl Client {
|
|||||||
|
|
||||||
frontend_events.push(Event::InventoryUpdated(event));
|
frontend_events.push(Event::InventoryUpdated(event));
|
||||||
},
|
},
|
||||||
ServerInGameMsg::TerrainChunkUpdate { key, chunk } => {
|
ServerInGame::TerrainChunkUpdate { key, chunk } => {
|
||||||
if let Ok(chunk) = chunk {
|
if let Ok(chunk) = chunk {
|
||||||
self.state.insert_chunk(key, *chunk);
|
self.state.insert_chunk(key, *chunk);
|
||||||
}
|
}
|
||||||
self.pending_chunks.remove(&key);
|
self.pending_chunks.remove(&key);
|
||||||
},
|
},
|
||||||
ServerInGameMsg::TerrainBlockUpdates(mut blocks) => {
|
ServerInGame::TerrainBlockUpdates(mut blocks) => {
|
||||||
blocks.drain().for_each(|(pos, block)| {
|
blocks.drain().for_each(|(pos, block)| {
|
||||||
self.state.set_block(pos, block);
|
self.state.set_block(pos, block);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
ServerInGameMsg::SetViewDistance(vd) => {
|
ServerInGame::SetViewDistance(vd) => {
|
||||||
self.view_distance = Some(vd);
|
self.view_distance = Some(vd);
|
||||||
frontend_events.push(Event::SetViewDistance(vd));
|
frontend_events.push(Event::SetViewDistance(vd));
|
||||||
},
|
},
|
||||||
ServerInGameMsg::Outcomes(outcomes) => {
|
ServerInGame::Outcomes(outcomes) => {
|
||||||
frontend_events.extend(outcomes.into_iter().map(Event::Outcome))
|
frontend_events.extend(outcomes.into_iter().map(Event::Outcome))
|
||||||
},
|
},
|
||||||
ServerInGameMsg::Knockback(impulse) => {
|
ServerInGame::Knockback(impulse) => {
|
||||||
self.state
|
self.state
|
||||||
.ecs()
|
.ecs()
|
||||||
.read_resource::<EventBus<LocalEvent>>()
|
.read_resource::<EventBus<LocalEvent>>()
|
||||||
@ -1409,24 +1396,24 @@ impl Client {
|
|||||||
|
|
||||||
fn handle_server_character_screen_msg(
|
fn handle_server_character_screen_msg(
|
||||||
&mut self,
|
&mut self,
|
||||||
msg: ServerCharacterScreenMsg,
|
msg: ServerCharacterScreen,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
match msg {
|
match msg {
|
||||||
ServerCharacterScreenMsg::CharacterListUpdate(character_list) => {
|
ServerCharacterScreen::CharacterListUpdate(character_list) => {
|
||||||
self.character_list.characters = character_list;
|
self.character_list.characters = character_list;
|
||||||
self.character_list.loading = false;
|
self.character_list.loading = false;
|
||||||
},
|
},
|
||||||
ServerCharacterScreenMsg::CharacterActionError(error) => {
|
ServerCharacterScreen::CharacterActionError(error) => {
|
||||||
warn!("CharacterActionError: {:?}.", error);
|
warn!("CharacterActionError: {:?}.", error);
|
||||||
self.character_list.error = Some(error);
|
self.character_list.error = Some(error);
|
||||||
},
|
},
|
||||||
ServerCharacterScreenMsg::CharacterDataLoadError(error) => {
|
ServerCharacterScreen::CharacterDataLoadError(error) => {
|
||||||
trace!("Handling join error by server");
|
trace!("Handling join error by server");
|
||||||
self.client_ingame = None;
|
self.in_game = None;
|
||||||
self.clean_state();
|
self.clean_state();
|
||||||
self.character_list.error = Some(error);
|
self.character_list.error = Some(error);
|
||||||
},
|
},
|
||||||
ServerCharacterScreenMsg::CharacterSuccess => {
|
ServerCharacterScreen::CharacterSuccess => {
|
||||||
debug!("client is now in ingame state on server");
|
debug!("client is now in ingame state on server");
|
||||||
if let Some(vd) = self.view_distance {
|
if let Some(vd) = self.view_distance {
|
||||||
self.set_view_distance(vd);
|
self.set_view_distance(vd);
|
||||||
@ -1439,7 +1426,7 @@ impl Client {
|
|||||||
fn handle_ping_msg(&mut self, msg: PingMsg) -> Result<(), Error> {
|
fn handle_ping_msg(&mut self, msg: PingMsg) -> Result<(), Error> {
|
||||||
match msg {
|
match msg {
|
||||||
PingMsg::Ping => {
|
PingMsg::Ping => {
|
||||||
self.ping_stream.send(PingMsg::Pong)?;
|
self.send_msg_err(PingMsg::Pong)?;
|
||||||
},
|
},
|
||||||
PingMsg::Pong => {
|
PingMsg::Pong => {
|
||||||
self.last_server_pong = self.state.get_time();
|
self.last_server_pong = self.state.get_time();
|
||||||
@ -1535,7 +1522,7 @@ impl Client {
|
|||||||
|
|
||||||
pub fn get_client_type(&self) -> ClientType { ClientType::Game }
|
pub fn get_client_type(&self) -> ClientType { ClientType::Game }
|
||||||
|
|
||||||
pub fn get_in_game(&self) -> Option<ClientIngame> { self.client_ingame }
|
pub fn get_in_game(&self) -> Option<ClientIngame> { self.in_game }
|
||||||
|
|
||||||
pub fn get_registered(&self) -> bool { self.registered }
|
pub fn get_registered(&self) -> bool { self.registered }
|
||||||
|
|
||||||
@ -1810,13 +1797,17 @@ impl Client {
|
|||||||
impl Drop for Client {
|
impl Drop for Client {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
trace!("Dropping client");
|
trace!("Dropping client");
|
||||||
if let Err(e) = self.general_stream.send(ClientGeneralMsg::Disconnect) {
|
if self.registered {
|
||||||
|
if let Err(e) = self.send_msg_err(ClientGeneral::Disconnect) {
|
||||||
warn!(
|
warn!(
|
||||||
?e,
|
?e,
|
||||||
"Error during drop of client, couldn't send disconnect package, is the connection \
|
"Error during drop of client, couldn't send disconnect package, is the \
|
||||||
already closed?",
|
connection already closed?",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
trace!("no disconnect msg necessary as client wasn't registered")
|
||||||
|
}
|
||||||
if let Err(e) = block_on(self.participant.take().unwrap().disconnect()) {
|
if let Err(e) = block_on(self.participant.take().unwrap().disconnect()) {
|
||||||
warn!(?e, "error when disconnecting, couldn't send all data");
|
warn!(?e, "error when disconnecting, couldn't send all data");
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{comp::group::Group, msg::ServerGeneralMsg, sync::Uid};
|
use crate::{comp::group::Group, msg::ServerGeneral, sync::Uid};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::Component;
|
use specs::Component;
|
||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
@ -118,11 +118,11 @@ impl<G> ChatType<G> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ChatType<String> {
|
impl ChatType<String> {
|
||||||
pub fn server_msg<S>(self, msg: S) -> ServerGeneralMsg
|
pub fn server_msg<S>(self, msg: S) -> ServerGeneral
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
ServerGeneralMsg::ChatMsg(self.chat_msg(msg))
|
ServerGeneral::ChatMsg(self.chat_msg(msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Stores chat text, type
|
// Stores chat text, type
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use super::PingMsg;
|
||||||
use crate::{
|
use crate::{
|
||||||
character::CharacterId,
|
character::CharacterId,
|
||||||
comp,
|
comp,
|
||||||
@ -7,25 +8,48 @@ use crate::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
///This struct contains all messages the client might send (on different
|
||||||
|
/// streams though). It's used to verify the correctness of the state in
|
||||||
|
/// debug_assertions
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ClientMsg {
|
||||||
|
///Send on the first connection ONCE to identify client intention for
|
||||||
|
/// server
|
||||||
|
Type(ClientType),
|
||||||
|
///Send ONCE to register/auth to the server
|
||||||
|
Register(ClientRegister),
|
||||||
|
///Msg only to send while in character screen, e.g. `CreateCharacter`
|
||||||
|
CharacterScreen(ClientCharacterScreen),
|
||||||
|
///Msg only to send while playing in game, e.g. `PlayerPositionUpdates`
|
||||||
|
InGame(ClientInGame),
|
||||||
|
///Msg that can be send ALWAYS as soon as we are registered, e.g. `Chat`
|
||||||
|
General(ClientGeneral),
|
||||||
|
Ping(PingMsg),
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
2nd Level Enums
|
||||||
|
*/
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum ClientType {
|
pub enum ClientType {
|
||||||
// Regular Client like Voxygen who plays the game
|
/// Regular Client like Voxygen who plays the game
|
||||||
Game,
|
Game,
|
||||||
// A Chatonly client, which doesn't want to connect via its character
|
/// A Chatonly client, which doesn't want to connect via its character
|
||||||
ChatOnly,
|
ChatOnly,
|
||||||
// A unprivileged bot, e.g. to request world information
|
/// A unprivileged bot, e.g. to request world information
|
||||||
// Or a privileged bot, e.g. to run admin commands used by server-cli
|
/// Or a privileged bot, e.g. to run admin commands used by server-cli
|
||||||
Bot { privileged: bool },
|
Bot { privileged: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct ClientRegisterMsg {
|
pub struct ClientRegister {
|
||||||
pub token_or_username: String,
|
pub token_or_username: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
//messages send by clients only valid when in character screen
|
//messages send by clients only valid when in character screen
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum ClientCharacterScreenMsg {
|
pub enum ClientCharacterScreen {
|
||||||
RequestCharacterList,
|
RequestCharacterList,
|
||||||
CreateCharacter {
|
CreateCharacter {
|
||||||
alias: String,
|
alias: String,
|
||||||
@ -39,7 +63,7 @@ pub enum ClientCharacterScreenMsg {
|
|||||||
|
|
||||||
//messages send by clients only valid when in game (with a character)
|
//messages send by clients only valid when in game (with a character)
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum ClientInGameMsg {
|
pub enum ClientInGame {
|
||||||
ControllerInputs(comp::ControllerInputs),
|
ControllerInputs(comp::ControllerInputs),
|
||||||
ControlEvent(comp::ControlEvent),
|
ControlEvent(comp::ControlEvent),
|
||||||
ControlAction(comp::ControlAction),
|
ControlAction(comp::ControlAction),
|
||||||
@ -62,8 +86,36 @@ pub enum ClientInGameMsg {
|
|||||||
|
|
||||||
/// Messages sent from the client to the server
|
/// Messages sent from the client to the server
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ClientGeneralMsg {
|
pub enum ClientGeneral {
|
||||||
ChatMsg(String),
|
ChatMsg(String),
|
||||||
Disconnect,
|
Disconnect,
|
||||||
Terminate,
|
Terminate,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
end of 2nd level Enums
|
||||||
|
*/
|
||||||
|
|
||||||
|
impl Into<ClientMsg> for ClientType {
|
||||||
|
fn into(self) -> ClientMsg { ClientMsg::Type(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ClientMsg> for ClientRegister {
|
||||||
|
fn into(self) -> ClientMsg { ClientMsg::Register(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ClientMsg> for ClientCharacterScreen {
|
||||||
|
fn into(self) -> ClientMsg { ClientMsg::CharacterScreen(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ClientMsg> for ClientInGame {
|
||||||
|
fn into(self) -> ClientMsg { ClientMsg::InGame(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ClientMsg> for ClientGeneral {
|
||||||
|
fn into(self) -> ClientMsg { ClientMsg::General(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ClientMsg> for PingMsg {
|
||||||
|
fn into(self) -> ClientMsg { ClientMsg::Ping(self) }
|
||||||
|
}
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod ecs_packet;
|
pub mod ecs_packet;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
pub mod world_packet;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use self::{
|
pub use self::{
|
||||||
client::{
|
client::{
|
||||||
ClientCharacterScreenMsg, ClientGeneralMsg, ClientInGameMsg, ClientRegisterMsg, ClientType,
|
ClientCharacterScreen, ClientGeneral, ClientInGame, ClientMsg, ClientRegister, ClientType,
|
||||||
},
|
},
|
||||||
ecs_packet::EcsCompPacket,
|
ecs_packet::EcsCompPacket,
|
||||||
server::{
|
server::{
|
||||||
CharacterInfo, DisconnectReason, InviteAnswer, Notification, PlayerInfo, PlayerListUpdate,
|
CharacterInfo, DisconnectReason, InviteAnswer, Notification, PlayerInfo, PlayerListUpdate,
|
||||||
RegisterError, ServerCharacterScreenMsg, ServerGeneralMsg, ServerInGameMsg, ServerInfo,
|
RegisterError, ServerCharacterScreen, ServerGeneral, ServerInGame, ServerInfo, ServerInit,
|
||||||
ServerInitMsg, ServerRegisterAnswerMsg,
|
ServerMsg, ServerRegisterAnswer,
|
||||||
},
|
},
|
||||||
|
world_packet::WorldMapMsg,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::EcsCompPacket;
|
use super::{EcsCompPacket, PingMsg};
|
||||||
use crate::{
|
use crate::{
|
||||||
character::CharacterItem,
|
character::CharacterItem,
|
||||||
comp,
|
comp,
|
||||||
@ -14,6 +14,31 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
///This struct contains all messages the server might send (on different
|
||||||
|
/// streams though)
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ServerMsg {
|
||||||
|
/// Basic info about server, send ONCE, clients need it to Register
|
||||||
|
Info(ServerInfo),
|
||||||
|
/// Initial data package, send BEFORE Register ONCE. Not Register relevant
|
||||||
|
Init(ServerInit),
|
||||||
|
/// Result to `ClientMsg::Register`. send ONCE
|
||||||
|
RegisterAnswer(ServerRegisterAnswer),
|
||||||
|
/// Msg only to send when client is on the character screen, e.g.
|
||||||
|
/// `CharacterListUpdate`
|
||||||
|
CharacterScreen(ServerCharacterScreen),
|
||||||
|
/// Msg only to send when client is playing in game, e.g.
|
||||||
|
/// `TerrainChunkUpdate`
|
||||||
|
InGame(ServerInGame),
|
||||||
|
///Msg that can be send ALWAYS as soon as client is registered, e.g. `Chat`
|
||||||
|
General(ServerGeneral),
|
||||||
|
Ping(PingMsg),
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
2nd Level Enums
|
||||||
|
*/
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ServerInfo {
|
pub struct ServerInfo {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -23,195 +48,26 @@ pub struct ServerInfo {
|
|||||||
pub auth_provider: Option<String>,
|
pub auth_provider: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inform the client of updates to the player list.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub enum PlayerListUpdate {
|
|
||||||
Init(HashMap<Uid, PlayerInfo>),
|
|
||||||
Add(Uid, PlayerInfo),
|
|
||||||
SelectedCharacter(Uid, CharacterInfo),
|
|
||||||
LevelChange(Uid, u32),
|
|
||||||
Admin(Uid, bool),
|
|
||||||
Remove(Uid),
|
|
||||||
Alias(Uid, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct PlayerInfo {
|
|
||||||
pub is_admin: bool,
|
|
||||||
pub is_online: bool,
|
|
||||||
pub player_alias: String,
|
|
||||||
pub character: Option<CharacterInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct CharacterInfo {
|
|
||||||
pub name: String,
|
|
||||||
pub level: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
/// World map information. Note that currently, we always send the whole thing
|
|
||||||
/// in one go, but the structure aims to try to provide information as locally
|
|
||||||
/// as possible, so that in the future we can split up large maps into multiple
|
|
||||||
/// WorldMapMsg fragments.
|
|
||||||
///
|
|
||||||
/// TODO: Update message format to make fragmentable, allowing us to send more
|
|
||||||
/// information without running into bandwidth issues.
|
|
||||||
///
|
|
||||||
/// TODO: Add information for rivers (currently, we just prerender them on the
|
|
||||||
/// server, but this is not a great solution for LoD. The map rendering code is
|
|
||||||
/// already set up to be able to take advantage of the river rendering being
|
|
||||||
/// split out, but the format is a little complicated for space reasons and it
|
|
||||||
/// may take some tweaking to get right, so we avoid sending it for now).
|
|
||||||
///
|
|
||||||
/// TODO: measure explicit compression schemes that might save space, e.g.
|
|
||||||
/// repeating the "small angles" optimization that works well on more detailed
|
|
||||||
/// shadow maps intended for height maps.
|
|
||||||
pub struct WorldMapMsg {
|
|
||||||
/// Log base 2 of world map dimensions (width × height) in chunks.
|
|
||||||
///
|
|
||||||
/// NOTE: Invariant: chunk count fits in a u16.
|
|
||||||
pub dimensions_lg: Vec2<u32>,
|
|
||||||
/// Sea level (used to provide a base altitude).
|
|
||||||
pub sea_level: f32,
|
|
||||||
/// Max height (used to scale altitudes).
|
|
||||||
pub max_height: f32,
|
|
||||||
/// RGB+A; the alpha channel is currently unused, but will be used in the
|
|
||||||
/// future. Entries are in the usual chunk order.
|
|
||||||
pub rgba: Vec<u32>,
|
|
||||||
/// Altitudes: bits 2 to 0 are unused, then bits 15 to 3 are used for
|
|
||||||
/// altitude. The remainder are currently unused, but we have plans to
|
|
||||||
/// use 7 bits for water depth (using an integer f7 encoding), and we
|
|
||||||
/// will find other uses for the remaining 12 bits.
|
|
||||||
pub alt: Vec<u32>,
|
|
||||||
/// Horizon mapping. This is a variant of shadow mapping that is
|
|
||||||
/// specifically designed for height maps; it takes advantage of their
|
|
||||||
/// regular structure (e.g. no holes) to compress all information needed
|
|
||||||
/// to decide when to cast a sharp shadow into a single nagle, the "horizon
|
|
||||||
/// angle." This is the smallest angle with the ground at which light can
|
|
||||||
/// pass through any occluders to reach the chunk, in some chosen
|
|
||||||
/// horizontal direction. This would not be sufficient for a more
|
|
||||||
/// complicated 3D structure, but it works for height maps since:
|
|
||||||
///
|
|
||||||
/// 1. they have no gaps, so as soon as light can shine through it will
|
|
||||||
/// always be able to do so, and
|
|
||||||
/// 2. we only care about lighting from the top, and only from the east and
|
|
||||||
/// west (since at a large scale like this we mostly just want to
|
|
||||||
/// handle variable sunlight; moonlight would present more challenges
|
|
||||||
/// but we currently have no plans to try to cast accurate shadows in
|
|
||||||
/// moonlight).
|
|
||||||
///
|
|
||||||
/// Our chosen format is two pairs of vectors,
|
|
||||||
/// with the first pair representing west-facing light (casting shadows on
|
|
||||||
/// the left side) and the second representing east-facing light
|
|
||||||
/// (casting shadows on the east side).
|
|
||||||
///
|
|
||||||
/// The pair of vectors consists of (with each vector in the usual chunk
|
|
||||||
/// order):
|
|
||||||
///
|
|
||||||
/// * Horizon angle pointing east (1 byte, scaled so 1 unit = 255° / 360).
|
|
||||||
/// We might consider switching to tangent if that represents the
|
|
||||||
/// information we care about better.
|
|
||||||
/// * Approximate (floor) height of maximal occluder. We currently use this
|
|
||||||
/// to try to deliver some approximation of soft shadows, which isn't that
|
|
||||||
/// big a deal on the world map but is probably needed in order to ensure
|
|
||||||
/// smooth transitions between chunks in LoD view. Additionally, when we
|
|
||||||
/// start using the shadow information to do local lighting on the world
|
|
||||||
/// map, we'll want a quick way to test where we can go out of shadow at
|
|
||||||
/// arbitrary heights (since the player and other entities cajn find
|
|
||||||
/// themselves far from the ground at times). While this is only an
|
|
||||||
/// approximation to a proper distance map, hopefully it will give us
|
|
||||||
/// something that feels reasonable enough for Veloren's style.
|
|
||||||
///
|
|
||||||
/// NOTE: On compression.
|
|
||||||
///
|
|
||||||
/// Horizon mapping has a lot of advantages for height maps (simple, easy to
|
|
||||||
/// understand, doesn't require any fancy math or approximation beyond
|
|
||||||
/// precision loss), though it loses a few of them by having to store
|
|
||||||
/// distance to occluder as well. However, just storing tons
|
|
||||||
/// and tons of regular shadow maps (153 for a full day cycle, stored at
|
|
||||||
/// irregular intervals) combined with clever explicit compression and
|
|
||||||
/// avoiding recording sharp local shadows (preferring retracing for
|
|
||||||
/// these), yielded a compression rate of under 3 bits per column! Since
|
|
||||||
/// we likely want to avoid per-column shadows for worlds of the sizes we
|
|
||||||
/// want, we'd still need to store *some* extra information to create
|
|
||||||
/// soft shadows, but it would still be nice to try to drive down our
|
|
||||||
/// size as much as possible given how compressible shadows of height
|
|
||||||
/// maps seem to be in practice. Therefore, we try to take advantage of the
|
|
||||||
/// way existing compression algorithms tend to work to see if we can
|
|
||||||
/// achieve significant gains without doing a lot of custom work.
|
|
||||||
///
|
|
||||||
/// Specifically, since our rays are cast east/west, we expect that for each
|
|
||||||
/// row, the horizon angles in each direction should be sequences of
|
|
||||||
/// monotonically increasing values (as chunks approach a tall
|
|
||||||
/// occluder), followed by sequences of no shadow, repeated
|
|
||||||
/// until the end of the map. Monotonic sequences and same-byte sequences
|
|
||||||
/// are usually easy to compress and existing algorithms are more likely
|
|
||||||
/// to be able to deal with them than jumbled data. If we were to keep
|
|
||||||
/// both directions in the same vector, off-the-shelf compression would
|
|
||||||
/// probably be less effective.
|
|
||||||
///
|
|
||||||
/// For related reasons, rather than storing distances as in a standard
|
|
||||||
/// distance map (which would lead to monotonically *decreasing* values
|
|
||||||
/// as we approached the occluder from a given direction), we store the
|
|
||||||
/// estimated *occluder height.* The idea here is that we replace the
|
|
||||||
/// monotonic sequences with constant sequences, which are extremely
|
|
||||||
/// straightforward to compress and mostly handled automatically by anything
|
|
||||||
/// that does run-length encoding (i.e. most off-the-shelf compression
|
|
||||||
/// algorithms).
|
|
||||||
///
|
|
||||||
/// We still need to benchmark this properly, as there's no guarantee our
|
|
||||||
/// current compression algorithms will actually work well on this data
|
|
||||||
/// in practice. It's possible that some other permutation (e.g. more
|
|
||||||
/// bits reserved for "distance to occluder" in exchange for an even
|
|
||||||
/// more predictible sequence) would end up compressing better than storing
|
|
||||||
/// angles, or that we don't need as much precision as we currently have
|
|
||||||
/// (256 possible angles).
|
|
||||||
pub horizons: [(Vec<u8>, Vec<u8>); 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub enum InviteAnswer {
|
|
||||||
Accepted,
|
|
||||||
Declined,
|
|
||||||
TimedOut,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub enum Notification {
|
|
||||||
WaypointSaved,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub enum DisconnectReason {
|
|
||||||
/// Server shut down
|
|
||||||
Shutdown,
|
|
||||||
/// Client sent disconnect message
|
|
||||||
Requested,
|
|
||||||
/// Client was kicked
|
|
||||||
Kicked(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reponse To ClientType
|
/// Reponse To ClientType
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[allow(clippy::clippy::large_enum_variant)]
|
#[allow(clippy::clippy::large_enum_variant)]
|
||||||
pub enum ServerInitMsg {
|
pub enum ServerInit {
|
||||||
TooManyPlayers,
|
TooManyPlayers,
|
||||||
GameSync {
|
GameSync {
|
||||||
entity_package: sync::EntityPackage<EcsCompPacket>,
|
entity_package: sync::EntityPackage<EcsCompPacket>,
|
||||||
time_of_day: state::TimeOfDay,
|
time_of_day: state::TimeOfDay,
|
||||||
max_group_size: u32,
|
max_group_size: u32,
|
||||||
client_timeout: Duration,
|
client_timeout: Duration,
|
||||||
world_map: WorldMapMsg,
|
world_map: crate::msg::world_packet::WorldMapMsg,
|
||||||
recipe_book: RecipeBook,
|
recipe_book: RecipeBook,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ServerRegisterAnswerMsg = Result<(), RegisterError>;
|
pub type ServerRegisterAnswer = Result<(), RegisterError>;
|
||||||
|
|
||||||
//Messages only allowed while client in character screen
|
//Messages only allowed while client in character screen
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ServerCharacterScreenMsg {
|
pub enum ServerCharacterScreen {
|
||||||
/// An error occurred while loading character data
|
/// An error occurred while loading character data
|
||||||
CharacterDataLoadError(String),
|
CharacterDataLoadError(String),
|
||||||
/// A list of characters belonging to the a authenticated player was sent
|
/// A list of characters belonging to the a authenticated player was sent
|
||||||
@ -223,7 +79,7 @@ pub enum ServerCharacterScreenMsg {
|
|||||||
|
|
||||||
//Messages only allowed while client is in game (with a character)
|
//Messages only allowed while client is in game (with a character)
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ServerInGameMsg {
|
pub enum ServerInGame {
|
||||||
GroupUpdate(comp::group::ChangeNotification<sync::Uid>),
|
GroupUpdate(comp::group::ChangeNotification<sync::Uid>),
|
||||||
// Indicate to the client that they are invited to join a group
|
// Indicate to the client that they are invited to join a group
|
||||||
GroupInvite {
|
GroupInvite {
|
||||||
@ -256,7 +112,7 @@ pub enum ServerInGameMsg {
|
|||||||
|
|
||||||
/// Messages sent from the server to the client
|
/// Messages sent from the server to the client
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ServerGeneralMsg {
|
pub enum ServerGeneral {
|
||||||
PlayerListUpdate(PlayerListUpdate),
|
PlayerListUpdate(PlayerListUpdate),
|
||||||
/// A message to go into the client chat box. The client is responsible for
|
/// A message to go into the client chat box. The client is responsible for
|
||||||
/// formatting the message and turning it into a speech bubble.
|
/// formatting the message and turning it into a speech bubble.
|
||||||
@ -272,6 +128,58 @@ pub enum ServerGeneralMsg {
|
|||||||
Notification(Notification),
|
Notification(Notification),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
end of 2nd level Enums
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Inform the client of updates to the player list.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum PlayerListUpdate {
|
||||||
|
Init(HashMap<Uid, PlayerInfo>),
|
||||||
|
Add(Uid, PlayerInfo),
|
||||||
|
SelectedCharacter(Uid, CharacterInfo),
|
||||||
|
LevelChange(Uid, u32),
|
||||||
|
Admin(Uid, bool),
|
||||||
|
Remove(Uid),
|
||||||
|
Alias(Uid, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PlayerInfo {
|
||||||
|
pub is_admin: bool,
|
||||||
|
pub is_online: bool,
|
||||||
|
pub player_alias: String,
|
||||||
|
pub character: Option<CharacterInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct CharacterInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub level: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum InviteAnswer {
|
||||||
|
Accepted,
|
||||||
|
Declined,
|
||||||
|
TimedOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum Notification {
|
||||||
|
WaypointSaved,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum DisconnectReason {
|
||||||
|
/// Server shut down
|
||||||
|
Shutdown,
|
||||||
|
/// Client sent disconnect message
|
||||||
|
Requested,
|
||||||
|
/// Client was kicked
|
||||||
|
Kicked(String),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub enum RegisterError {
|
pub enum RegisterError {
|
||||||
AlreadyLoggedIn,
|
AlreadyLoggedIn,
|
||||||
@ -286,6 +194,34 @@ impl From<AuthClientError> for RegisterError {
|
|||||||
fn from(err: AuthClientError) -> Self { Self::AuthError(err.to_string()) }
|
fn from(err: AuthClientError) -> Self { Self::AuthError(err.to_string()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<comp::ChatMsg> for ServerGeneralMsg {
|
impl From<comp::ChatMsg> for ServerGeneral {
|
||||||
fn from(v: comp::ChatMsg) -> Self { ServerGeneralMsg::ChatMsg(v) }
|
fn from(v: comp::ChatMsg) -> Self { ServerGeneral::ChatMsg(v) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ServerMsg> for ServerInfo {
|
||||||
|
fn into(self) -> ServerMsg { ServerMsg::Info(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ServerMsg> for ServerInit {
|
||||||
|
fn into(self) -> ServerMsg { ServerMsg::Init(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ServerMsg> for ServerRegisterAnswer {
|
||||||
|
fn into(self) -> ServerMsg { ServerMsg::RegisterAnswer(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ServerMsg> for ServerCharacterScreen {
|
||||||
|
fn into(self) -> ServerMsg { ServerMsg::CharacterScreen(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ServerMsg> for ServerInGame {
|
||||||
|
fn into(self) -> ServerMsg { ServerMsg::InGame(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ServerMsg> for ServerGeneral {
|
||||||
|
fn into(self) -> ServerMsg { ServerMsg::General(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ServerMsg> for PingMsg {
|
||||||
|
fn into(self) -> ServerMsg { ServerMsg::Ping(self) }
|
||||||
}
|
}
|
||||||
|
123
common/src/msg/world_packet.rs
Normal file
123
common/src/msg/world_packet.rs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
/// World map information. Note that currently, we always send the whole thing
|
||||||
|
/// in one go, but the structure aims to try to provide information as locally
|
||||||
|
/// as possible, so that in the future we can split up large maps into multiple
|
||||||
|
/// WorldMapMsg fragments.
|
||||||
|
///
|
||||||
|
/// TODO: Update message format to make fragmentable, allowing us to send more
|
||||||
|
/// information without running into bandwidth issues.
|
||||||
|
///
|
||||||
|
/// TODO: Add information for rivers (currently, we just prerender them on the
|
||||||
|
/// server, but this is not a great solution for LoD. The map rendering code is
|
||||||
|
/// already set up to be able to take advantage of the river rendering being
|
||||||
|
/// split out, but the format is a little complicated for space reasons and it
|
||||||
|
/// may take some tweaking to get right, so we avoid sending it for now).
|
||||||
|
///
|
||||||
|
/// TODO: measure explicit compression schemes that might save space, e.g.
|
||||||
|
/// repeating the "small angles" optimization that works well on more detailed
|
||||||
|
/// shadow maps intended for height maps.
|
||||||
|
pub struct WorldMapMsg {
|
||||||
|
/// Log base 2 of world map dimensions (width × height) in chunks.
|
||||||
|
///
|
||||||
|
/// NOTE: Invariant: chunk count fits in a u16.
|
||||||
|
pub dimensions_lg: Vec2<u32>,
|
||||||
|
/// Sea level (used to provide a base altitude).
|
||||||
|
pub sea_level: f32,
|
||||||
|
/// Max height (used to scale altitudes).
|
||||||
|
pub max_height: f32,
|
||||||
|
/// RGB+A; the alpha channel is currently unused, but will be used in the
|
||||||
|
/// future. Entries are in the usual chunk order.
|
||||||
|
pub rgba: Vec<u32>,
|
||||||
|
/// Altitudes: bits 2 to 0 are unused, then bits 15 to 3 are used for
|
||||||
|
/// altitude. The remainder are currently unused, but we have plans to
|
||||||
|
/// use 7 bits for water depth (using an integer f7 encoding), and we
|
||||||
|
/// will find other uses for the remaining 12 bits.
|
||||||
|
pub alt: Vec<u32>,
|
||||||
|
/// Horizon mapping. This is a variant of shadow mapping that is
|
||||||
|
/// specifically designed for height maps; it takes advantage of their
|
||||||
|
/// regular structure (e.g. no holes) to compress all information needed
|
||||||
|
/// to decide when to cast a sharp shadow into a single nagle, the "horizon
|
||||||
|
/// angle." This is the smallest angle with the ground at which light can
|
||||||
|
/// pass through any occluders to reach the chunk, in some chosen
|
||||||
|
/// horizontal direction. This would not be sufficient for a more
|
||||||
|
/// complicated 3D structure, but it works for height maps since:
|
||||||
|
///
|
||||||
|
/// 1. they have no gaps, so as soon as light can shine through it will
|
||||||
|
/// always be able to do so, and
|
||||||
|
/// 2. we only care about lighting from the top, and only from the east and
|
||||||
|
/// west (since at a large scale like this we mostly just want to
|
||||||
|
/// handle variable sunlight; moonlight would present more challenges
|
||||||
|
/// but we currently have no plans to try to cast accurate shadows in
|
||||||
|
/// moonlight).
|
||||||
|
///
|
||||||
|
/// Our chosen format is two pairs of vectors,
|
||||||
|
/// with the first pair representing west-facing light (casting shadows on
|
||||||
|
/// the left side) and the second representing east-facing light
|
||||||
|
/// (casting shadows on the east side).
|
||||||
|
///
|
||||||
|
/// The pair of vectors consists of (with each vector in the usual chunk
|
||||||
|
/// order):
|
||||||
|
///
|
||||||
|
/// * Horizon angle pointing east (1 byte, scaled so 1 unit = 255° / 360).
|
||||||
|
/// We might consider switching to tangent if that represents the
|
||||||
|
/// information we care about better.
|
||||||
|
/// * Approximate (floor) height of maximal occluder. We currently use this
|
||||||
|
/// to try to deliver some approximation of soft shadows, which isn't that
|
||||||
|
/// big a deal on the world map but is probably needed in order to ensure
|
||||||
|
/// smooth transitions between chunks in LoD view. Additionally, when we
|
||||||
|
/// start using the shadow information to do local lighting on the world
|
||||||
|
/// map, we'll want a quick way to test where we can go out of shadow at
|
||||||
|
/// arbitrary heights (since the player and other entities cajn find
|
||||||
|
/// themselves far from the ground at times). While this is only an
|
||||||
|
/// approximation to a proper distance map, hopefully it will give us
|
||||||
|
/// something that feels reasonable enough for Veloren's style.
|
||||||
|
///
|
||||||
|
/// NOTE: On compression.
|
||||||
|
///
|
||||||
|
/// Horizon mapping has a lot of advantages for height maps (simple, easy to
|
||||||
|
/// understand, doesn't require any fancy math or approximation beyond
|
||||||
|
/// precision loss), though it loses a few of them by having to store
|
||||||
|
/// distance to occluder as well. However, just storing tons
|
||||||
|
/// and tons of regular shadow maps (153 for a full day cycle, stored at
|
||||||
|
/// irregular intervals) combined with clever explicit compression and
|
||||||
|
/// avoiding recording sharp local shadows (preferring retracing for
|
||||||
|
/// these), yielded a compression rate of under 3 bits per column! Since
|
||||||
|
/// we likely want to avoid per-column shadows for worlds of the sizes we
|
||||||
|
/// want, we'd still need to store *some* extra information to create
|
||||||
|
/// soft shadows, but it would still be nice to try to drive down our
|
||||||
|
/// size as much as possible given how compressible shadows of height
|
||||||
|
/// maps seem to be in practice. Therefore, we try to take advantage of the
|
||||||
|
/// way existing compression algorithms tend to work to see if we can
|
||||||
|
/// achieve significant gains without doing a lot of custom work.
|
||||||
|
///
|
||||||
|
/// Specifically, since our rays are cast east/west, we expect that for each
|
||||||
|
/// row, the horizon angles in each direction should be sequences of
|
||||||
|
/// monotonically increasing values (as chunks approach a tall
|
||||||
|
/// occluder), followed by sequences of no shadow, repeated
|
||||||
|
/// until the end of the map. Monotonic sequences and same-byte sequences
|
||||||
|
/// are usually easy to compress and existing algorithms are more likely
|
||||||
|
/// to be able to deal with them than jumbled data. If we were to keep
|
||||||
|
/// both directions in the same vector, off-the-shelf compression would
|
||||||
|
/// probably be less effective.
|
||||||
|
///
|
||||||
|
/// For related reasons, rather than storing distances as in a standard
|
||||||
|
/// distance map (which would lead to monotonically *decreasing* values
|
||||||
|
/// as we approached the occluder from a given direction), we store the
|
||||||
|
/// estimated *occluder height.* The idea here is that we replace the
|
||||||
|
/// monotonic sequences with constant sequences, which are extremely
|
||||||
|
/// straightforward to compress and mostly handled automatically by anything
|
||||||
|
/// that does run-length encoding (i.e. most off-the-shelf compression
|
||||||
|
/// algorithms).
|
||||||
|
///
|
||||||
|
/// We still need to benchmark this properly, as there's no guarantee our
|
||||||
|
/// current compression algorithms will actually work well on this data
|
||||||
|
/// in practice. It's possible that some other permutation (e.g. more
|
||||||
|
/// bits reserved for "distance to occluder" in exchange for an even
|
||||||
|
/// more predictible sequence) would end up compressing better than storing
|
||||||
|
/// angles, or that we don't need as much precision as we currently have
|
||||||
|
/// (256 possible angles).
|
||||||
|
pub horizons: [(Vec<u8>, Vec<u8>); 2],
|
||||||
|
}
|
@ -1,16 +1,16 @@
|
|||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use common::msg::{
|
use common::msg::{
|
||||||
ClientCharacterScreenMsg, ClientGeneralMsg, ClientInGameMsg, ClientIngame, ClientType, PingMsg,
|
ClientCharacterScreen, ClientGeneral, ClientInGame, ClientIngame, ClientType, PingMsg,
|
||||||
ServerCharacterScreenMsg, ServerGeneralMsg, ServerInGameMsg, ServerInitMsg,
|
ServerMsg,
|
||||||
};
|
};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use network::{MessageBuffer, Participant, Stream};
|
use network::{Participant, Stream};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use specs::{Component, FlaggedStorage};
|
use specs::{Component, FlaggedStorage};
|
||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
Arc, Mutex,
|
Mutex,
|
||||||
};
|
};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -20,7 +20,7 @@ pub struct Client {
|
|||||||
pub client_type: ClientType,
|
pub client_type: ClientType,
|
||||||
pub in_game: Option<ClientIngame>,
|
pub in_game: Option<ClientIngame>,
|
||||||
pub participant: Mutex<Option<Participant>>,
|
pub participant: Mutex<Option<Participant>>,
|
||||||
pub singleton_stream: Stream,
|
pub general_stream: Stream,
|
||||||
pub ping_stream: Stream,
|
pub ping_stream: Stream,
|
||||||
pub register_stream: Stream,
|
pub register_stream: Stream,
|
||||||
pub character_screen_stream: Stream,
|
pub character_screen_stream: Stream,
|
||||||
@ -44,6 +44,7 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
fn internal_send_raw(b: &AtomicBool, s: &mut Stream, msg: Arc<MessageBuffer>) {
|
fn internal_send_raw(b: &AtomicBool, s: &mut Stream, msg: Arc<MessageBuffer>) {
|
||||||
if !b.load(Ordering::Relaxed) {
|
if !b.load(Ordering::Relaxed) {
|
||||||
if let Err(e) = s.send_raw(msg) {
|
if let Err(e) = s.send_raw(msg) {
|
||||||
@ -52,29 +53,32 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub fn send_init(&mut self, msg: ServerInitMsg) {
|
pub fn send_msg<S>(&mut self, msg: S)
|
||||||
Self::internal_send(&self.network_error, &mut self.register_stream, msg);
|
where
|
||||||
}
|
S: Into<ServerMsg>,
|
||||||
|
{
|
||||||
pub fn send_msg(&mut self, msg: ServerGeneralMsg) {
|
const ERR: &str = "Dont do that, thats only done once at the start, no via this class";
|
||||||
Self::internal_send(&self.network_error, &mut self.singleton_stream, msg);
|
match msg.into() {
|
||||||
}
|
ServerMsg::Info(_) => panic!(ERR),
|
||||||
|
ServerMsg::Init(_) => panic!(ERR),
|
||||||
pub fn send_in_game(&mut self, msg: ServerInGameMsg) {
|
ServerMsg::RegisterAnswer(msg) => {
|
||||||
Self::internal_send(&self.network_error, &mut self.in_game_stream, msg);
|
Self::internal_send(&self.network_error, &mut self.register_stream, &msg)
|
||||||
}
|
},
|
||||||
|
ServerMsg::CharacterScreen(msg) => {
|
||||||
pub fn send_character_screen(&mut self, msg: ServerCharacterScreenMsg) {
|
Self::internal_send(&self.network_error, &mut self.character_screen_stream, &msg)
|
||||||
Self::internal_send(&self.network_error, &mut self.character_screen_stream, msg);
|
},
|
||||||
}
|
ServerMsg::InGame(msg) => {
|
||||||
|
Self::internal_send(&self.network_error, &mut self.in_game_stream, &msg)
|
||||||
pub fn send_ping(&mut self, msg: PingMsg) {
|
},
|
||||||
Self::internal_send(&self.network_error, &mut self.ping_stream, msg);
|
ServerMsg::General(msg) => {
|
||||||
}
|
Self::internal_send(&self.network_error, &mut self.general_stream, &msg)
|
||||||
|
},
|
||||||
pub fn send_msg_raw(&mut self, msg: Arc<MessageBuffer>) {
|
ServerMsg::Ping(msg) => {
|
||||||
Self::internal_send_raw(&self.network_error, &mut self.singleton_stream, msg);
|
Self::internal_send(&self.network_error, &mut self.ping_stream, &msg)
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn internal_recv<M: DeserializeOwned>(
|
pub async fn internal_recv<M: DeserializeOwned>(
|
||||||
@ -95,15 +99,15 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn recv_msg(&mut self) -> Result<ClientGeneralMsg, Error> {
|
pub async fn recv_msg(&mut self) -> Result<ClientGeneral, Error> {
|
||||||
Self::internal_recv(&self.network_error, &mut self.singleton_stream).await
|
Self::internal_recv(&self.network_error, &mut self.general_stream).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn recv_in_game_msg(&mut self) -> Result<ClientInGameMsg, Error> {
|
pub async fn recv_in_game_msg(&mut self) -> Result<ClientInGame, Error> {
|
||||||
Self::internal_recv(&self.network_error, &mut self.in_game_stream).await
|
Self::internal_recv(&self.network_error, &mut self.in_game_stream).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn recv_character_screen_msg(&mut self) -> Result<ClientCharacterScreenMsg, Error> {
|
pub async fn recv_character_screen_msg(&mut self) -> Result<ClientCharacterScreen, Error> {
|
||||||
Self::internal_recv(&self.network_error, &mut self.character_screen_stream).await
|
Self::internal_recv(&self.network_error, &mut self.character_screen_stream).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use common::{
|
|||||||
cmd::{ChatCommand, CHAT_COMMANDS, CHAT_SHORTCUTS},
|
cmd::{ChatCommand, CHAT_COMMANDS, CHAT_SHORTCUTS},
|
||||||
comp::{self, ChatType, Item, LightEmitter, WaypointArea},
|
comp::{self, ChatType, Item, LightEmitter, WaypointArea},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
msg::{DisconnectReason, Notification, PlayerListUpdate, ServerGeneralMsg, ServerInGameMsg},
|
msg::{DisconnectReason, Notification, PlayerListUpdate, ServerGeneral, ServerInGame},
|
||||||
npc::{self, get_npc_name},
|
npc::{self, get_npc_name},
|
||||||
state::TimeOfDay,
|
state::TimeOfDay,
|
||||||
sync::{Uid, WorldSyncExt},
|
sync::{Uid, WorldSyncExt},
|
||||||
@ -504,7 +504,7 @@ fn handle_alias(
|
|||||||
ecs.read_storage::<comp::Player>().get(target),
|
ecs.read_storage::<comp::Player>().get(target),
|
||||||
old_alias_optional,
|
old_alias_optional,
|
||||||
) {
|
) {
|
||||||
let msg = ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::Alias(
|
let msg = ServerGeneral::PlayerListUpdate(PlayerListUpdate::Alias(
|
||||||
*uid,
|
*uid,
|
||||||
player.alias.clone(),
|
player.alias.clone(),
|
||||||
));
|
));
|
||||||
@ -669,9 +669,7 @@ fn handle_spawn(
|
|||||||
.try_map(|e| uids.get(e).copied())
|
.try_map(|e| uids.get(e).copied())
|
||||||
.map(|g| (g, c))
|
.map(|g| (g, c))
|
||||||
})
|
})
|
||||||
.map(|(g, c)| {
|
.map(|(g, c)| c.send_msg(ServerInGame::GroupUpdate(g)));
|
||||||
c.send_in_game(ServerInGameMsg::GroupUpdate(g))
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if let Some(group) = match alignment {
|
} else if let Some(group) = match alignment {
|
||||||
@ -1161,7 +1159,7 @@ fn handle_waypoint(
|
|||||||
server.notify_client(client, ChatType::CommandInfo.server_msg("Waypoint saved!"));
|
server.notify_client(client, ChatType::CommandInfo.server_msg("Waypoint saved!"));
|
||||||
server.notify_client(
|
server.notify_client(
|
||||||
client,
|
client,
|
||||||
ServerGeneralMsg::Notification(Notification::WaypointSaved),
|
ServerGeneral::Notification(Notification::WaypointSaved),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
None => server.notify_client(
|
None => server.notify_client(
|
||||||
@ -1198,7 +1196,7 @@ fn handle_adminify(
|
|||||||
ecs.write_storage().insert(player, comp::Admin).is_ok()
|
ecs.write_storage().insert(player, comp::Admin).is_ok()
|
||||||
};
|
};
|
||||||
// Update player list so the player shows up as admin in client chat.
|
// Update player list so the player shows up as admin in client chat.
|
||||||
let msg = ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::Admin(
|
let msg = ServerGeneral::PlayerListUpdate(PlayerListUpdate::Admin(
|
||||||
*ecs.read_storage::<Uid>()
|
*ecs.read_storage::<Uid>()
|
||||||
.get(player)
|
.get(player)
|
||||||
.expect("Player should have uid"),
|
.expect("Player should have uid"),
|
||||||
@ -1591,7 +1589,7 @@ fn find_target(
|
|||||||
ecs: &specs::World,
|
ecs: &specs::World,
|
||||||
opt_alias: Option<String>,
|
opt_alias: Option<String>,
|
||||||
fallback: EcsEntity,
|
fallback: EcsEntity,
|
||||||
) -> Result<EcsEntity, ServerGeneralMsg> {
|
) -> Result<EcsEntity, ServerGeneral> {
|
||||||
if let Some(alias) = opt_alias {
|
if let Some(alias) = opt_alias {
|
||||||
(&ecs.entities(), &ecs.read_storage::<comp::Player>())
|
(&ecs.entities(), &ecs.read_storage::<comp::Player>())
|
||||||
.join()
|
.join()
|
||||||
@ -1663,7 +1661,7 @@ fn handle_set_level(
|
|||||||
.expect("Failed to get uid for player");
|
.expect("Failed to get uid for player");
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.notify_registered_clients(ServerGeneralMsg::PlayerListUpdate(
|
.notify_registered_clients(ServerGeneral::PlayerListUpdate(
|
||||||
PlayerListUpdate::LevelChange(uid, lvl),
|
PlayerListUpdate::LevelChange(uid, lvl),
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -1903,7 +1901,7 @@ fn kick_player(server: &mut Server, target_player: EcsEntity, reason: &str) {
|
|||||||
.emit_now(ServerEvent::ClientDisconnect(target_player));
|
.emit_now(ServerEvent::ClientDisconnect(target_player));
|
||||||
server.notify_client(
|
server.notify_client(
|
||||||
target_player,
|
target_player,
|
||||||
ServerGeneralMsg::Disconnect(DisconnectReason::Kicked(reason.to_string())),
|
ServerGeneral::Disconnect(DisconnectReason::Kicked(reason.to_string())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ impl ConnectionHandler {
|
|||||||
client_type,
|
client_type,
|
||||||
in_game: None,
|
in_game: None,
|
||||||
participant: std::sync::Mutex::new(Some(participant)),
|
participant: std::sync::Mutex::new(Some(participant)),
|
||||||
singleton_stream: general_stream,
|
general_stream,
|
||||||
ping_stream,
|
ping_stream,
|
||||||
register_stream,
|
register_stream,
|
||||||
in_game_stream,
|
in_game_stream,
|
||||||
|
@ -12,7 +12,7 @@ use common::{
|
|||||||
Player, Pos, Stats,
|
Player, Pos, Stats,
|
||||||
},
|
},
|
||||||
lottery::Lottery,
|
lottery::Lottery,
|
||||||
msg::{PlayerListUpdate, ServerGeneralMsg, ServerInGameMsg},
|
msg::{PlayerListUpdate, ServerGeneral, ServerInGame},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
state::BlockChange,
|
state::BlockChange,
|
||||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
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>();
|
let mut clients = state.ecs().write_storage::<Client>();
|
||||||
if let Some(client) = clients.get_mut(entity) {
|
if let Some(client) = clients.get_mut(entity) {
|
||||||
client.send_in_game(ServerInGameMsg::Knockback(impulse));
|
client.send_msg(ServerInGame::Knockback(impulse));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,7 +656,7 @@ pub fn handle_level_up(server: &mut Server, entity: EcsEntity, new_level: u32) {
|
|||||||
|
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.notify_registered_clients(ServerGeneralMsg::PlayerListUpdate(
|
.notify_registered_clients(ServerGeneral::PlayerListUpdate(
|
||||||
PlayerListUpdate::LevelChange(*uid, new_level),
|
PlayerListUpdate::LevelChange(*uid, new_level),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use common::{
|
|||||||
group::{self, Group, GroupManager, Invite, PendingInvites},
|
group::{self, Group, GroupManager, Invite, PendingInvites},
|
||||||
ChatType, GroupManip,
|
ChatType, GroupManip,
|
||||||
},
|
},
|
||||||
msg::{InviteAnswer, ServerInGameMsg},
|
msg::{InviteAnswer, ServerInGame},
|
||||||
sync,
|
sync,
|
||||||
sync::WorldSyncExt,
|
sync::WorldSyncExt,
|
||||||
};
|
};
|
||||||
@ -155,7 +155,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
|||||||
(clients.get_mut(invitee), uids.get(entity).copied())
|
(clients.get_mut(invitee), uids.get(entity).copied())
|
||||||
{
|
{
|
||||||
if send_invite() {
|
if send_invite() {
|
||||||
client.send_in_game(ServerInGameMsg::GroupInvite {
|
client.send_msg(ServerInGame::GroupInvite {
|
||||||
inviter,
|
inviter,
|
||||||
timeout: PRESENTED_INVITE_TIMEOUT_DUR,
|
timeout: PRESENTED_INVITE_TIMEOUT_DUR,
|
||||||
});
|
});
|
||||||
@ -171,7 +171,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
|||||||
// Notify inviter that the invite is pending
|
// Notify inviter that the invite is pending
|
||||||
if invite_sent {
|
if invite_sent {
|
||||||
if let Some(client) = clients.get_mut(entity) {
|
if let Some(client) = clients.get_mut(entity) {
|
||||||
client.send_in_game(ServerInGameMsg::InvitePending(uid));
|
client.send_msg(ServerInGame::InvitePending(uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -196,7 +196,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
|||||||
if let (Some(client), Some(target)) =
|
if let (Some(client), Some(target)) =
|
||||||
(clients.get_mut(inviter), uids.get(entity).copied())
|
(clients.get_mut(inviter), uids.get(entity).copied())
|
||||||
{
|
{
|
||||||
client.send_in_game(ServerInGameMsg::InviteComplete {
|
client.send_msg(ServerInGame::InviteComplete {
|
||||||
target,
|
target,
|
||||||
answer: InviteAnswer::Accepted,
|
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())
|
.try_map(|e| uids.get(e).copied())
|
||||||
.map(|g| (g, c))
|
.map(|g| (g, c))
|
||||||
})
|
})
|
||||||
.map(|(g, c)| c.send_in_game(ServerInGameMsg::GroupUpdate(g)));
|
.map(|(g, c)| c.send_msg(ServerInGame::GroupUpdate(g)));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -244,7 +244,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
|||||||
if let (Some(client), Some(target)) =
|
if let (Some(client), Some(target)) =
|
||||||
(clients.get_mut(inviter), uids.get(entity).copied())
|
(clients.get_mut(inviter), uids.get(entity).copied())
|
||||||
{
|
{
|
||||||
client.send_in_game(ServerInGameMsg::InviteComplete {
|
client.send_msg(ServerInGame::InviteComplete {
|
||||||
target,
|
target,
|
||||||
answer: InviteAnswer::Declined,
|
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())
|
.try_map(|e| uids.get(e).copied())
|
||||||
.map(|g| (g, c))
|
.map(|g| (g, c))
|
||||||
})
|
})
|
||||||
.map(|(g, c)| c.send_in_game(ServerInGameMsg::GroupUpdate(g)));
|
.map(|(g, c)| c.send_msg(ServerInGame::GroupUpdate(g)));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -336,7 +336,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
|||||||
.try_map(|e| uids.get(e).copied())
|
.try_map(|e| uids.get(e).copied())
|
||||||
.map(|g| (g, c))
|
.map(|g| (g, c))
|
||||||
})
|
})
|
||||||
.map(|(g, c)| c.send_in_game(ServerInGameMsg::GroupUpdate(g)));
|
.map(|(g, c)| c.send_msg(ServerInGame::GroupUpdate(g)));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -410,7 +410,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
|
|||||||
.try_map(|e| uids.get(e).copied())
|
.try_map(|e| uids.get(e).copied())
|
||||||
.map(|g| (g, c))
|
.map(|g| (g, c))
|
||||||
})
|
})
|
||||||
.map(|(g, c)| c.send_in_game(ServerInGameMsg::GroupUpdate(g)));
|
.map(|(g, c)| c.send_msg(ServerInGame::GroupUpdate(g)));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// Tell them they are the leader
|
// Tell them they are the leader
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{self, item},
|
comp::{self, item},
|
||||||
msg::ServerGeneralMsg,
|
msg::ServerGeneral,
|
||||||
sync::{Uid, WorldSyncExt},
|
sync::{Uid, WorldSyncExt},
|
||||||
};
|
};
|
||||||
use specs::{world::WorldExt, Entity as EcsEntity};
|
use specs::{world::WorldExt, Entity as EcsEntity};
|
||||||
@ -116,7 +116,7 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) {
|
|||||||
let mut clients = ecs.write_storage::<Client>();
|
let mut clients = ecs.write_storage::<Client>();
|
||||||
if clients.get_mut(possesse).is_none() {
|
if clients.get_mut(possesse).is_none() {
|
||||||
if let Some(mut client) = clients.remove(possessor) {
|
if let Some(mut client) = clients.remove(possessor) {
|
||||||
client.send_msg(ServerGeneralMsg::SetPlayerEntity(possesse_uid));
|
client.send_msg(ServerGeneral::SetPlayerEntity(possesse_uid));
|
||||||
clients
|
clients
|
||||||
.insert(possesse, client)
|
.insert(possesse, client)
|
||||||
.err()
|
.err()
|
||||||
|
@ -5,7 +5,7 @@ use common::{
|
|||||||
slot::{self, Slot},
|
slot::{self, Slot},
|
||||||
Pos, MAX_PICKUP_RANGE_SQR,
|
Pos, MAX_PICKUP_RANGE_SQR,
|
||||||
},
|
},
|
||||||
msg::ServerInGameMsg,
|
msg::ServerInGame,
|
||||||
recipe::default_recipe_book,
|
recipe::default_recipe_book,
|
||||||
sync::{Uid, WorldSyncExt},
|
sync::{Uid, WorldSyncExt},
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
@ -281,9 +281,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
.map(|g| (g, c))
|
.map(|g| (g, c))
|
||||||
})
|
})
|
||||||
.map(|(g, c)| {
|
.map(|(g, c)| {
|
||||||
c.send_in_game(
|
c.send_msg(ServerInGame::GroupUpdate(g))
|
||||||
ServerInGameMsg::GroupUpdate(g),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
comp::{group, Player},
|
comp::{group, Player},
|
||||||
msg::{PlayerListUpdate, ServerGeneralMsg, ServerInGameMsg},
|
msg::{PlayerListUpdate, ServerGeneral, ServerInGame},
|
||||||
span,
|
span,
|
||||||
sync::{Uid, UidAllocator},
|
sync::{Uid, UidAllocator},
|
||||||
};
|
};
|
||||||
@ -34,7 +34,7 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) {
|
|||||||
if let (Some(mut client), Some(uid), Some(player)) = (maybe_client, maybe_uid, maybe_player) {
|
if let (Some(mut client), Some(uid), Some(player)) = (maybe_client, maybe_uid, maybe_player) {
|
||||||
// Tell client its request was successful
|
// Tell client its request was successful
|
||||||
client.in_game = None;
|
client.in_game = None;
|
||||||
client.send_in_game(ServerInGameMsg::ExitInGameSuccess);
|
client.send_msg(ServerInGame::ExitInGameSuccess);
|
||||||
|
|
||||||
let entity_builder = state.ecs_mut().create_entity().with(client).with(player);
|
let entity_builder = state.ecs_mut().create_entity().with(client).with(player);
|
||||||
|
|
||||||
@ -130,9 +130,9 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event
|
|||||||
) {
|
) {
|
||||||
state.notify_registered_clients(comp::ChatType::Offline(*uid).server_msg(""));
|
state.notify_registered_clients(comp::ChatType::Offline(*uid).server_msg(""));
|
||||||
|
|
||||||
state.notify_registered_clients(ServerGeneralMsg::PlayerListUpdate(
|
state.notify_registered_clients(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Remove(
|
||||||
PlayerListUpdate::Remove(*uid),
|
*uid,
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure to remove the player from the logged in list. (See LoginProvider)
|
// Make sure to remove the player from the logged in list. (See LoginProvider)
|
||||||
|
@ -47,8 +47,8 @@ use common::{
|
|||||||
comp::{self, ChatType},
|
comp::{self, ChatType},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
msg::{
|
msg::{
|
||||||
server::WorldMapMsg, ClientType, DisconnectReason, ServerCharacterScreenMsg,
|
ClientType, DisconnectReason, ServerCharacterScreen, ServerGeneral, ServerInfo, ServerInit,
|
||||||
ServerGeneralMsg, ServerInGameMsg, ServerInfo, ServerInitMsg,
|
ServerMsg, WorldMapMsg,
|
||||||
},
|
},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
recipe::default_recipe_book,
|
recipe::default_recipe_book,
|
||||||
@ -525,13 +525,13 @@ impl Server {
|
|||||||
.messages()
|
.messages()
|
||||||
.for_each(|query_result| match query_result.result {
|
.for_each(|query_result| match query_result.result {
|
||||||
CharacterLoaderResponseType::CharacterList(result) => match result {
|
CharacterLoaderResponseType::CharacterList(result) => match result {
|
||||||
Ok(character_list_data) => self.notify_character_screen_client(
|
Ok(character_list_data) => self.notify_client(
|
||||||
query_result.entity,
|
query_result.entity,
|
||||||
ServerCharacterScreenMsg::CharacterListUpdate(character_list_data),
|
ServerCharacterScreen::CharacterListUpdate(character_list_data),
|
||||||
),
|
),
|
||||||
Err(error) => self.notify_character_screen_client(
|
Err(error) => self.notify_client(
|
||||||
query_result.entity,
|
query_result.entity,
|
||||||
ServerCharacterScreenMsg::CharacterActionError(error.to_string()),
|
ServerCharacterScreen::CharacterActionError(error.to_string()),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
CharacterLoaderResponseType::CharacterData(result) => {
|
CharacterLoaderResponseType::CharacterData(result) => {
|
||||||
@ -544,9 +544,9 @@ impl Server {
|
|||||||
// We failed to load data for the character from the DB. Notify the
|
// 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
|
// client to push the state back to character selection, with the error
|
||||||
// to display
|
// to display
|
||||||
self.notify_character_screen_client(
|
self.notify_client(
|
||||||
query_result.entity,
|
query_result.entity,
|
||||||
ServerCharacterScreenMsg::CharacterDataLoadError(error.to_string()),
|
ServerCharacterScreen::CharacterDataLoadError(error.to_string()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Clean up the entity data on the server
|
// Clean up the entity data on the server
|
||||||
@ -815,7 +815,7 @@ impl Server {
|
|||||||
?client.participant,
|
?client.participant,
|
||||||
"to many players, wont allow participant to connect"
|
"to many players, wont allow participant to connect"
|
||||||
);
|
);
|
||||||
client.register_stream.send(ServerInitMsg::TooManyPlayers)?;
|
client.register_stream.send(ServerInit::TooManyPlayers)?;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -839,7 +839,7 @@ impl Server {
|
|||||||
.get_mut(entity)
|
.get_mut(entity)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.register_stream
|
.register_stream
|
||||||
.send(ServerInitMsg::GameSync {
|
.send(ServerInit::GameSync {
|
||||||
// Send client their entity
|
// Send client their entity
|
||||||
entity_package: TrackedComps::fetch(&self.state.ecs())
|
entity_package: TrackedComps::fetch(&self.state.ecs())
|
||||||
.create_entity_package(entity, None, None, None),
|
.create_entity_package(entity, None, None, None),
|
||||||
@ -858,32 +858,14 @@ impl Server {
|
|||||||
|
|
||||||
pub fn notify_client<S>(&self, entity: EcsEntity, msg: S)
|
pub fn notify_client<S>(&self, entity: EcsEntity, msg: S)
|
||||||
where
|
where
|
||||||
S: Into<ServerGeneralMsg>,
|
S: Into<ServerMsg>,
|
||||||
{
|
{
|
||||||
if let Some(client) = self.state.ecs().write_storage::<Client>().get_mut(entity) {
|
if let Some(client) = self.state.ecs().write_storage::<Client>().get_mut(entity) {
|
||||||
client.send_msg(msg.into())
|
client.send_msg(msg.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify_in_game_client<S>(&self, entity: EcsEntity, msg: S)
|
pub fn notify_registered_clients(&mut self, msg: ServerGeneral) {
|
||||||
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_character_screen_client<S>(&self, entity: EcsEntity, msg: S)
|
|
||||||
where
|
|
||||||
S: Into<ServerCharacterScreenMsg>,
|
|
||||||
{
|
|
||||||
if let Some(client) = self.state.ecs().write_storage::<Client>().get_mut(entity) {
|
|
||||||
client.send_character_screen(msg.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn notify_registered_clients(&mut self, msg: ServerGeneralMsg) {
|
|
||||||
self.state.notify_registered_clients(msg);
|
self.state.notify_registered_clients(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -963,7 +945,7 @@ impl Server {
|
|||||||
impl Drop for Server {
|
impl Drop for Server {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.state
|
self.state
|
||||||
.notify_registered_clients(ServerGeneralMsg::Disconnect(DisconnectReason::Shutdown));
|
.notify_registered_clients(ServerGeneral::Disconnect(DisconnectReason::Shutdown));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ use common::{
|
|||||||
comp,
|
comp,
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
msg::{
|
msg::{
|
||||||
CharacterInfo, ClientIngame, PlayerListUpdate, ServerCharacterScreenMsg, ServerGeneralMsg,
|
CharacterInfo, ClientIngame, PlayerListUpdate, ServerCharacterScreen, ServerGeneral,
|
||||||
ServerInGameMsg,
|
ServerInGame, ServerMsg,
|
||||||
},
|
},
|
||||||
state::State,
|
state::State,
|
||||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
sync::{Uid, UidAllocator, WorldSyncExt},
|
||||||
@ -62,7 +62,8 @@ pub trait StateExt {
|
|||||||
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents);
|
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents);
|
||||||
/// Iterates over registered clients and send each `ServerMsg`
|
/// Iterates over registered clients and send each `ServerMsg`
|
||||||
fn send_chat(&self, msg: comp::UnresolvedChatMsg);
|
fn send_chat(&self, msg: comp::UnresolvedChatMsg);
|
||||||
fn notify_registered_clients(&self, msg: ServerGeneralMsg);
|
fn notify_registered_clients(&self, msg: ServerGeneral);
|
||||||
|
fn notify_in_game_clients(&self, msg: ServerInGame);
|
||||||
/// Delete an entity, recording the deletion in [`DeletedEntities`]
|
/// Delete an entity, recording the deletion in [`DeletedEntities`]
|
||||||
fn delete_entity_recorded(
|
fn delete_entity_recorded(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -220,7 +221,7 @@ impl StateExt for State {
|
|||||||
// Tell the client its request was successful.
|
// Tell the client its request was successful.
|
||||||
if let Some(client) = self.ecs().write_storage::<Client>().get_mut(entity) {
|
if let Some(client) = self.ecs().write_storage::<Client>().get_mut(entity) {
|
||||||
client.in_game = Some(ClientIngame::Character);
|
client.in_game = Some(ClientIngame::Character);
|
||||||
client.send_character_screen(ServerCharacterScreenMsg::CharacterSuccess)
|
client.send_msg(ServerCharacterScreen::CharacterSuccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +230,7 @@ impl StateExt for State {
|
|||||||
|
|
||||||
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
|
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
|
||||||
// Notify clients of a player list update
|
// Notify clients of a player list update
|
||||||
self.notify_registered_clients(ServerGeneralMsg::PlayerListUpdate(
|
self.notify_registered_clients(ServerGeneral::PlayerListUpdate(
|
||||||
PlayerListUpdate::SelectedCharacter(player_uid, CharacterInfo {
|
PlayerListUpdate::SelectedCharacter(player_uid, CharacterInfo {
|
||||||
name: String::from(&stats.name),
|
name: String::from(&stats.name),
|
||||||
level: stats.level.level(),
|
level: stats.level.level(),
|
||||||
@ -276,7 +277,7 @@ impl StateExt for State {
|
|||||||
| comp::ChatType::Kill(_, _)
|
| comp::ChatType::Kill(_, _)
|
||||||
| comp::ChatType::Meta
|
| comp::ChatType::Meta
|
||||||
| comp::ChatType::World(_) => {
|
| comp::ChatType::World(_) => {
|
||||||
self.notify_registered_clients(ServerGeneralMsg::ChatMsg(resolved_msg))
|
self.notify_registered_clients(ServerGeneral::ChatMsg(resolved_msg))
|
||||||
},
|
},
|
||||||
comp::ChatType::Tell(u, t) => {
|
comp::ChatType::Tell(u, t) => {
|
||||||
for (client, uid) in (
|
for (client, uid) in (
|
||||||
@ -286,7 +287,7 @@ impl StateExt for State {
|
|||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
if uid == u || uid == t {
|
if uid == u || uid == t {
|
||||||
client.send_msg(ServerGeneralMsg::ChatMsg(resolved_msg.clone()));
|
client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -298,7 +299,7 @@ impl StateExt for State {
|
|||||||
if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) {
|
if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) {
|
||||||
for (client, pos) in (&mut ecs.write_storage::<Client>(), &positions).join() {
|
for (client, pos) in (&mut ecs.write_storage::<Client>(), &positions).join() {
|
||||||
if is_within(comp::ChatMsg::SAY_DISTANCE, pos, speaker_pos) {
|
if is_within(comp::ChatMsg::SAY_DISTANCE, pos, speaker_pos) {
|
||||||
client.send_msg(ServerGeneralMsg::ChatMsg(resolved_msg.clone()));
|
client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,7 +311,7 @@ impl StateExt for State {
|
|||||||
if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) {
|
if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) {
|
||||||
for (client, pos) in (&mut ecs.write_storage::<Client>(), &positions).join() {
|
for (client, pos) in (&mut ecs.write_storage::<Client>(), &positions).join() {
|
||||||
if is_within(comp::ChatMsg::REGION_DISTANCE, pos, speaker_pos) {
|
if is_within(comp::ChatMsg::REGION_DISTANCE, pos, speaker_pos) {
|
||||||
client.send_msg(ServerGeneralMsg::ChatMsg(resolved_msg.clone()));
|
client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -322,7 +323,7 @@ impl StateExt for State {
|
|||||||
if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) {
|
if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) {
|
||||||
for (client, pos) in (&mut ecs.write_storage::<Client>(), &positions).join() {
|
for (client, pos) in (&mut ecs.write_storage::<Client>(), &positions).join() {
|
||||||
if is_within(comp::ChatMsg::NPC_DISTANCE, pos, speaker_pos) {
|
if is_within(comp::ChatMsg::NPC_DISTANCE, pos, speaker_pos) {
|
||||||
client.send_msg(ServerGeneralMsg::ChatMsg(resolved_msg.clone()));
|
client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,7 +337,7 @@ impl StateExt for State {
|
|||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
if s == &faction.0 {
|
if s == &faction.0 {
|
||||||
client.send_msg(ServerGeneralMsg::ChatMsg(resolved_msg.clone()));
|
client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -348,7 +349,7 @@ impl StateExt for State {
|
|||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
if g == group {
|
if g == group {
|
||||||
client.send_msg(ServerGeneralMsg::ChatMsg(resolved_msg.clone()));
|
client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -356,7 +357,8 @@ impl StateExt for State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sends the message to all connected clients
|
/// Sends the message to all connected clients
|
||||||
fn notify_registered_clients(&self, msg: ServerGeneralMsg) {
|
fn notify_registered_clients(&self, msg: ServerGeneral) {
|
||||||
|
let msg: ServerMsg = msg.into();
|
||||||
for client in (&mut self.ecs().write_storage::<Client>())
|
for client in (&mut self.ecs().write_storage::<Client>())
|
||||||
.join()
|
.join()
|
||||||
.filter(|c| c.registered)
|
.filter(|c| c.registered)
|
||||||
@ -365,6 +367,17 @@ impl StateExt for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends the message to all clients playing in game
|
||||||
|
fn notify_in_game_clients(&self, msg: ServerInGame) {
|
||||||
|
let msg: ServerMsg = msg.into();
|
||||||
|
for client in (&mut self.ecs().write_storage::<Client>())
|
||||||
|
.join()
|
||||||
|
.filter(|c| c.in_game.is_some())
|
||||||
|
{
|
||||||
|
client.send_msg(msg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn delete_entity_recorded(
|
fn delete_entity_recorded(
|
||||||
&mut self,
|
&mut self,
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
@ -388,7 +401,7 @@ impl StateExt for State {
|
|||||||
.try_map(|e| uids.get(e).copied())
|
.try_map(|e| uids.get(e).copied())
|
||||||
.map(|g| (g, c))
|
.map(|g| (g, c))
|
||||||
})
|
})
|
||||||
.map(|(g, c)| c.send_in_game(ServerInGameMsg::GroupUpdate(g)));
|
.map(|(g, c)| c.send_msg(ServerInGame::GroupUpdate(g)));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Player, Pos, Vel},
|
comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Player, Pos, Vel},
|
||||||
msg::{ServerGeneralMsg, ServerInGameMsg},
|
msg::{ServerGeneral, ServerInGame},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
region::{Event as RegionEvent, RegionMap},
|
region::{Event as RegionEvent, RegionMap},
|
||||||
span,
|
span,
|
||||||
@ -128,14 +128,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
(uid, pos, velocities.get(entity), orientations.get(entity))
|
(uid, pos, velocities.get(entity), orientations.get(entity))
|
||||||
})
|
})
|
||||||
}) {
|
}) {
|
||||||
let create_msg = ServerGeneralMsg::CreateEntity(
|
let create_msg =
|
||||||
tracked_comps.create_entity_package(
|
ServerGeneral::CreateEntity(tracked_comps.create_entity_package(
|
||||||
entity,
|
entity,
|
||||||
Some(*pos),
|
Some(*pos),
|
||||||
vel.copied(),
|
vel.copied(),
|
||||||
ori.copied(),
|
ori.copied(),
|
||||||
),
|
));
|
||||||
);
|
|
||||||
for (client, regions, client_entity, _) in &mut subscribers {
|
for (client, regions, client_entity, _) in &mut subscribers {
|
||||||
if maybe_key
|
if maybe_key
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -158,7 +157,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.map(|key| !regions.contains(key))
|
.map(|key| !regions.contains(key))
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
client.send_msg(ServerGeneralMsg::DeleteEntity(uid));
|
client.send_msg(ServerGeneral::DeleteEntity(uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,14 +174,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
.take_deleted_in_region(key)
|
.take_deleted_in_region(key)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
let entity_sync_msg = ServerGeneralMsg::EntitySync(entity_sync_package);
|
let entity_sync_msg = ServerGeneral::EntitySync(entity_sync_package);
|
||||||
let comp_sync_msg = ServerGeneralMsg::CompSync(comp_sync_package);
|
let comp_sync_msg = ServerGeneral::CompSync(comp_sync_package);
|
||||||
subscribers.iter_mut().for_each(move |(client, _, _, _)| {
|
subscribers.iter_mut().for_each(move |(client, _, _, _)| {
|
||||||
client.send_msg(entity_sync_msg.clone());
|
client.send_msg(entity_sync_msg.clone());
|
||||||
client.send_msg(comp_sync_msg.clone());
|
client.send_msg(comp_sync_msg.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut send_msg = |msg: ServerGeneralMsg,
|
let mut send_msg = |msg: ServerGeneral,
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
force_update: Option<&ForceUpdate>,
|
force_update: Option<&ForceUpdate>,
|
||||||
@ -288,7 +287,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
send_msg(
|
send_msg(
|
||||||
ServerGeneralMsg::CompSync(comp_sync_package),
|
ServerGeneral::CompSync(comp_sync_package),
|
||||||
entity,
|
entity,
|
||||||
pos,
|
pos,
|
||||||
force_update,
|
force_update,
|
||||||
@ -312,7 +311,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
for uid in &deleted {
|
for uid in &deleted {
|
||||||
client.send_msg(ServerGeneralMsg::DeleteEntity(Uid(*uid)));
|
client.send_msg(ServerGeneral::DeleteEntity(Uid(*uid)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,7 +320,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
// Sync inventories
|
// Sync inventories
|
||||||
for (client, inventory, update) in (&mut clients, &inventories, &inventory_updates).join() {
|
for (client, inventory, update) in (&mut clients, &inventories, &inventory_updates).join() {
|
||||||
client.send_in_game(ServerInGameMsg::InventoryUpdate(
|
client.send_msg(ServerInGame::InventoryUpdate(
|
||||||
inventory.clone(),
|
inventory.clone(),
|
||||||
update.event(),
|
update.event(),
|
||||||
));
|
));
|
||||||
@ -342,7 +341,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if !outcomes.is_empty() {
|
if !outcomes.is_empty() {
|
||||||
client.send_in_game(ServerInGameMsg::Outcomes(outcomes));
|
client.send_msg(ServerInGame::Outcomes(outcomes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
outcomes.clear();
|
outcomes.clear();
|
||||||
@ -354,7 +353,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Sync resources
|
// Sync resources
|
||||||
// TODO: doesn't really belong in this system (rename system or create another
|
// TODO: doesn't really belong in this system (rename system or create another
|
||||||
// system?)
|
// system?)
|
||||||
let tof_msg = ServerGeneralMsg::TimeOfDay(*time_of_day);
|
let tof_msg = ServerGeneral::TimeOfDay(*time_of_day);
|
||||||
for client in (&mut clients).join() {
|
for client in (&mut clients).join() {
|
||||||
client.send_msg(tof_msg.clone());
|
client.send_msg(tof_msg.clone());
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use super::SysTimer;
|
|||||||
use crate::client::Client;
|
use crate::client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
comp::group::{Invite, PendingInvites},
|
comp::group::{Invite, PendingInvites},
|
||||||
msg::{InviteAnswer, ServerInGameMsg},
|
msg::{InviteAnswer, ServerInGame},
|
||||||
span,
|
span,
|
||||||
sync::Uid,
|
sync::Uid,
|
||||||
};
|
};
|
||||||
@ -54,7 +54,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
if let (Some(client), Some(target)) =
|
if let (Some(client), Some(target)) =
|
||||||
(clients.get_mut(*inviter), uids.get(invitee).copied())
|
(clients.get_mut(*inviter), uids.get(invitee).copied())
|
||||||
{
|
{
|
||||||
client.send_in_game(ServerInGameMsg::InviteComplete {
|
client.send_msg(ServerInGame::InviteComplete {
|
||||||
target,
|
target,
|
||||||
answer: InviteAnswer::TimedOut,
|
answer: InviteAnswer::TimedOut,
|
||||||
})
|
})
|
||||||
|
@ -15,10 +15,10 @@ use common::{
|
|||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
msg::{
|
msg::{
|
||||||
validate_chat_msg, CharacterInfo, ChatMsgValidationError, ClientCharacterScreenMsg,
|
validate_chat_msg, CharacterInfo, ChatMsgValidationError, ClientCharacterScreen,
|
||||||
ClientGeneralMsg, ClientInGameMsg, ClientIngame, ClientRegisterMsg, DisconnectReason,
|
ClientGeneral, ClientInGame, ClientIngame, ClientRegister, DisconnectReason, PingMsg,
|
||||||
PingMsg, PlayerInfo, PlayerListUpdate, RegisterError, ServerCharacterScreenMsg,
|
PlayerInfo, PlayerListUpdate, RegisterError, ServerCharacterScreen, ServerGeneral,
|
||||||
ServerGeneralMsg, ServerInGameMsg, ServerRegisterAnswerMsg, MAX_BYTES_CHAT_MSG,
|
ServerInGame, ServerRegisterAnswer, MAX_BYTES_CHAT_MSG,
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
state::{BlockChange, Time},
|
state::{BlockChange, Time},
|
||||||
@ -45,10 +45,10 @@ impl Sys {
|
|||||||
player_metrics: &ReadExpect<'_, PlayerMetrics>,
|
player_metrics: &ReadExpect<'_, PlayerMetrics>,
|
||||||
uids: &ReadStorage<'_, Uid>,
|
uids: &ReadStorage<'_, Uid>,
|
||||||
chat_modes: &ReadStorage<'_, ChatMode>,
|
chat_modes: &ReadStorage<'_, ChatMode>,
|
||||||
msg: ClientGeneralMsg,
|
msg: ClientGeneral,
|
||||||
) -> Result<(), crate::error::Error> {
|
) -> Result<(), crate::error::Error> {
|
||||||
match msg {
|
match msg {
|
||||||
ClientGeneralMsg::ChatMsg(message) => {
|
ClientGeneral::ChatMsg(message) => {
|
||||||
if client.registered {
|
if client.registered {
|
||||||
match validate_chat_msg(&message) {
|
match validate_chat_msg(&message) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
@ -68,10 +68,10 @@ impl Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientGeneralMsg::Disconnect => {
|
ClientGeneral::Disconnect => {
|
||||||
client.send_msg(ServerGeneralMsg::Disconnect(DisconnectReason::Requested));
|
client.send_msg(ServerGeneral::Disconnect(DisconnectReason::Requested));
|
||||||
},
|
},
|
||||||
ClientGeneralMsg::Terminate => {
|
ClientGeneral::Terminate => {
|
||||||
debug!(?entity, "Client send message to termitate session");
|
debug!(?entity, "Client send message to termitate session");
|
||||||
player_metrics
|
player_metrics
|
||||||
.clients_disconnected
|
.clients_disconnected
|
||||||
@ -100,24 +100,24 @@ impl Sys {
|
|||||||
players: &mut WriteStorage<'_, Player>,
|
players: &mut WriteStorage<'_, Player>,
|
||||||
controllers: &mut WriteStorage<'_, Controller>,
|
controllers: &mut WriteStorage<'_, Controller>,
|
||||||
settings: &Read<'_, Settings>,
|
settings: &Read<'_, Settings>,
|
||||||
msg: ClientInGameMsg,
|
msg: ClientInGame,
|
||||||
) -> Result<(), crate::error::Error> {
|
) -> Result<(), crate::error::Error> {
|
||||||
if client.in_game.is_none() {
|
if client.in_game.is_none() {
|
||||||
debug!(?entity, "client is not in_game, ignoring msg");
|
debug!(?entity, "client is not in_game, ignoring msg");
|
||||||
trace!(?msg, "ignored msg content");
|
trace!(?msg, "ignored msg content");
|
||||||
if matches!(msg, ClientInGameMsg::TerrainChunkRequest{ .. }) {
|
if matches!(msg, ClientInGame::TerrainChunkRequest{ .. }) {
|
||||||
network_metrics.chunks_request_dropped.inc();
|
network_metrics.chunks_request_dropped.inc();
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
match msg {
|
match msg {
|
||||||
// Go back to registered state (char selection screen)
|
// Go back to registered state (char selection screen)
|
||||||
ClientInGameMsg::ExitInGame => {
|
ClientInGame::ExitInGame => {
|
||||||
client.in_game = None;
|
client.in_game = None;
|
||||||
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
||||||
client.send_in_game(ServerInGameMsg::ExitInGameSuccess);
|
client.send_msg(ServerInGame::ExitInGameSuccess);
|
||||||
},
|
},
|
||||||
ClientInGameMsg::SetViewDistance(view_distance) => {
|
ClientInGame::SetViewDistance(view_distance) => {
|
||||||
players.get_mut(entity).map(|player| {
|
players.get_mut(entity).map(|player| {
|
||||||
player.view_distance = Some(
|
player.view_distance = Some(
|
||||||
settings
|
settings
|
||||||
@ -133,19 +133,19 @@ impl Sys {
|
|||||||
.map(|max| view_distance > max)
|
.map(|max| view_distance > max)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
client.send_in_game(ServerInGameMsg::SetViewDistance(
|
client.send_msg(ServerInGame::SetViewDistance(
|
||||||
settings.max_view_distance.unwrap_or(0),
|
settings.max_view_distance.unwrap_or(0),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientInGameMsg::ControllerInputs(inputs) => {
|
ClientInGame::ControllerInputs(inputs) => {
|
||||||
if let Some(ClientIngame::Character) = client.in_game {
|
if let Some(ClientIngame::Character) = client.in_game {
|
||||||
if let Some(controller) = controllers.get_mut(entity) {
|
if let Some(controller) = controllers.get_mut(entity) {
|
||||||
controller.inputs.update_with_new(inputs);
|
controller.inputs.update_with_new(inputs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientInGameMsg::ControlEvent(event) => {
|
ClientInGame::ControlEvent(event) => {
|
||||||
if let Some(ClientIngame::Character) = client.in_game {
|
if let Some(ClientIngame::Character) = client.in_game {
|
||||||
// Skip respawn if client entity is alive
|
// Skip respawn if client entity is alive
|
||||||
if let ControlEvent::Respawn = event {
|
if let ControlEvent::Respawn = event {
|
||||||
@ -159,14 +159,14 @@ impl Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientInGameMsg::ControlAction(event) => {
|
ClientInGame::ControlAction(event) => {
|
||||||
if let Some(ClientIngame::Character) = client.in_game {
|
if let Some(ClientIngame::Character) = client.in_game {
|
||||||
if let Some(controller) = controllers.get_mut(entity) {
|
if let Some(controller) = controllers.get_mut(entity) {
|
||||||
controller.actions.push(event);
|
controller.actions.push(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientInGameMsg::PlayerPhysics { pos, vel, ori } => {
|
ClientInGame::PlayerPhysics { pos, vel, ori } => {
|
||||||
if let Some(ClientIngame::Character) = client.in_game {
|
if let Some(ClientIngame::Character) = client.in_game {
|
||||||
if force_updates.get(entity).is_none()
|
if force_updates.get(entity).is_none()
|
||||||
&& stats.get(entity).map_or(true, |s| !s.is_dead)
|
&& stats.get(entity).map_or(true, |s| !s.is_dead)
|
||||||
@ -177,17 +177,17 @@ impl Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientInGameMsg::BreakBlock(pos) => {
|
ClientInGame::BreakBlock(pos) => {
|
||||||
if let Some(block) = can_build.get(entity).and_then(|_| terrain.get(pos).ok()) {
|
if let Some(block) = can_build.get(entity).and_then(|_| terrain.get(pos).ok()) {
|
||||||
block_changes.set(pos, block.into_vacant());
|
block_changes.set(pos, block.into_vacant());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientInGameMsg::PlaceBlock(pos, block) => {
|
ClientInGame::PlaceBlock(pos, block) => {
|
||||||
if can_build.get(entity).is_some() {
|
if can_build.get(entity).is_some() {
|
||||||
block_changes.try_set(pos, block);
|
block_changes.try_set(pos, block);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientInGameMsg::TerrainChunkRequest { key } => {
|
ClientInGame::TerrainChunkRequest { key } => {
|
||||||
let in_vd = if let (Some(view_distance), Some(pos)) = (
|
let in_vd = if let (Some(view_distance), Some(pos)) = (
|
||||||
players.get(entity).and_then(|p| p.view_distance),
|
players.get(entity).and_then(|p| p.view_distance),
|
||||||
positions.get(entity),
|
positions.get(entity),
|
||||||
@ -203,7 +203,7 @@ impl Sys {
|
|||||||
match terrain.get_key(key) {
|
match terrain.get_key(key) {
|
||||||
Some(chunk) => {
|
Some(chunk) => {
|
||||||
network_metrics.chunks_served_from_memory.inc();
|
network_metrics.chunks_served_from_memory.inc();
|
||||||
client.send_in_game(ServerInGameMsg::TerrainChunkUpdate {
|
client.send_msg(ServerInGame::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
chunk: Ok(Box::new(chunk.clone())),
|
chunk: Ok(Box::new(chunk.clone())),
|
||||||
})
|
})
|
||||||
@ -217,17 +217,17 @@ impl Sys {
|
|||||||
network_metrics.chunks_request_dropped.inc();
|
network_metrics.chunks_request_dropped.inc();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientInGameMsg::UnlockSkill(skill) => {
|
ClientInGame::UnlockSkill(skill) => {
|
||||||
stats
|
stats
|
||||||
.get_mut(entity)
|
.get_mut(entity)
|
||||||
.map(|s| s.skill_set.unlock_skill(skill));
|
.map(|s| s.skill_set.unlock_skill(skill));
|
||||||
},
|
},
|
||||||
ClientInGameMsg::RefundSkill(skill) => {
|
ClientInGame::RefundSkill(skill) => {
|
||||||
stats
|
stats
|
||||||
.get_mut(entity)
|
.get_mut(entity)
|
||||||
.map(|s| s.skill_set.refund_skill(skill));
|
.map(|s| s.skill_set.refund_skill(skill));
|
||||||
},
|
},
|
||||||
ClientInGameMsg::UnlockSkillGroup(skill_group_type) => {
|
ClientInGame::UnlockSkillGroup(skill_group_type) => {
|
||||||
stats
|
stats
|
||||||
.get_mut(entity)
|
.get_mut(entity)
|
||||||
.map(|s| s.skill_set.unlock_skill_group(skill_group_type));
|
.map(|s| s.skill_set.unlock_skill_group(skill_group_type));
|
||||||
@ -247,18 +247,18 @@ impl Sys {
|
|||||||
players: &mut WriteStorage<'_, Player>,
|
players: &mut WriteStorage<'_, Player>,
|
||||||
editable_settings: &ReadExpect<'_, EditableSettings>,
|
editable_settings: &ReadExpect<'_, EditableSettings>,
|
||||||
alias_validator: &ReadExpect<'_, AliasValidator>,
|
alias_validator: &ReadExpect<'_, AliasValidator>,
|
||||||
msg: ClientCharacterScreenMsg,
|
msg: ClientCharacterScreen,
|
||||||
) -> Result<(), crate::error::Error> {
|
) -> Result<(), crate::error::Error> {
|
||||||
match msg {
|
match msg {
|
||||||
// Request spectator state
|
// Request spectator state
|
||||||
ClientCharacterScreenMsg::Spectate => {
|
ClientCharacterScreen::Spectate => {
|
||||||
if client.registered {
|
if client.registered {
|
||||||
client.in_game = Some(ClientIngame::Spectator)
|
client.in_game = Some(ClientIngame::Spectator)
|
||||||
} else {
|
} else {
|
||||||
debug!("dropped Spectate msg from unregistered client");
|
debug!("dropped Spectate msg from unregistered client");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientCharacterScreenMsg::Character(character_id) => {
|
ClientCharacterScreen::Character(character_id) => {
|
||||||
if client.registered && client.in_game.is_none() {
|
if client.registered && client.in_game.is_none() {
|
||||||
// Only send login message if it wasn't already
|
// Only send login message if it wasn't already
|
||||||
// sent previously
|
// sent previously
|
||||||
@ -301,11 +301,9 @@ impl Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
client.send_character_screen(
|
client.send_msg(ServerCharacterScreen::CharacterDataLoadError(
|
||||||
ServerCharacterScreenMsg::CharacterDataLoadError(String::from(
|
String::from("Failed to fetch player entity"),
|
||||||
"Failed to fetch player entity",
|
))
|
||||||
)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let registered = client.registered;
|
let registered = client.registered;
|
||||||
@ -313,15 +311,15 @@ impl Sys {
|
|||||||
debug!(?registered, ?in_game, "dropped Character msg from client");
|
debug!(?registered, ?in_game, "dropped Character msg from client");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientCharacterScreenMsg::RequestCharacterList => {
|
ClientCharacterScreen::RequestCharacterList => {
|
||||||
if let Some(player) = players.get(entity) {
|
if let Some(player) = players.get(entity) {
|
||||||
character_loader.load_character_list(entity, player.uuid().to_string())
|
character_loader.load_character_list(entity, player.uuid().to_string())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientCharacterScreenMsg::CreateCharacter { alias, tool, body } => {
|
ClientCharacterScreen::CreateCharacter { alias, tool, body } => {
|
||||||
if let Err(error) = alias_validator.validate(&alias) {
|
if let Err(error) = alias_validator.validate(&alias) {
|
||||||
debug!(?error, ?alias, "denied alias as it contained a banned word");
|
debug!(?error, ?alias, "denied alias as it contained a banned word");
|
||||||
client.send_character_screen(ServerCharacterScreenMsg::CharacterActionError(
|
client.send_msg(ServerCharacterScreen::CharacterActionError(
|
||||||
error.to_string(),
|
error.to_string(),
|
||||||
));
|
));
|
||||||
} else if let Some(player) = players.get(entity) {
|
} else if let Some(player) = players.get(entity) {
|
||||||
@ -335,7 +333,7 @@ impl Sys {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientCharacterScreenMsg::DeleteCharacter(character_id) => {
|
ClientCharacterScreen::DeleteCharacter(character_id) => {
|
||||||
if let Some(player) = players.get(entity) {
|
if let Some(player) = players.get(entity) {
|
||||||
character_loader.delete_character(
|
character_loader.delete_character(
|
||||||
entity,
|
entity,
|
||||||
@ -351,7 +349,7 @@ impl Sys {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn handle_ping_msg(client: &mut Client, msg: PingMsg) -> Result<(), crate::error::Error> {
|
fn handle_ping_msg(client: &mut Client, msg: PingMsg) -> Result<(), crate::error::Error> {
|
||||||
match msg {
|
match msg {
|
||||||
PingMsg::Ping => client.send_ping(PingMsg::Pong),
|
PingMsg::Ping => client.send_msg(PingMsg::Pong),
|
||||||
PingMsg::Pong => {},
|
PingMsg::Pong => {},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -368,7 +366,7 @@ impl Sys {
|
|||||||
admins: &mut WriteStorage<'_, Admin>,
|
admins: &mut WriteStorage<'_, Admin>,
|
||||||
players: &mut WriteStorage<'_, Player>,
|
players: &mut WriteStorage<'_, Player>,
|
||||||
editable_settings: &ReadExpect<'_, EditableSettings>,
|
editable_settings: &ReadExpect<'_, EditableSettings>,
|
||||||
msg: ClientRegisterMsg,
|
msg: ClientRegister,
|
||||||
) -> Result<(), crate::error::Error> {
|
) -> Result<(), crate::error::Error> {
|
||||||
let (username, uuid) = match login_provider.try_login(
|
let (username, uuid) = match login_provider.try_login(
|
||||||
&msg.token_or_username,
|
&msg.token_or_username,
|
||||||
@ -379,7 +377,7 @@ impl Sys {
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
client
|
client
|
||||||
.register_stream
|
.register_stream
|
||||||
.send(ServerRegisterAnswerMsg::Err(err))?;
|
.send(ServerRegisterAnswer::Err(err))?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
},
|
},
|
||||||
Ok((username, uuid)) => (username, uuid),
|
Ok((username, uuid)) => (username, uuid),
|
||||||
@ -391,9 +389,9 @@ impl Sys {
|
|||||||
|
|
||||||
if !player.is_valid() {
|
if !player.is_valid() {
|
||||||
// Invalid player
|
// Invalid player
|
||||||
client.register_stream.send(ServerRegisterAnswerMsg::Err(
|
client
|
||||||
RegisterError::InvalidCharacter,
|
.register_stream
|
||||||
))?;
|
.send(ServerRegisterAnswer::Err(RegisterError::InvalidCharacter))?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,12 +408,10 @@ impl Sys {
|
|||||||
|
|
||||||
// Tell the client its request was successful.
|
// Tell the client its request was successful.
|
||||||
client.registered = true;
|
client.registered = true;
|
||||||
client
|
client.register_stream.send(ServerRegisterAnswer::Ok(()))?;
|
||||||
.register_stream
|
|
||||||
.send(ServerRegisterAnswerMsg::Ok(()))?;
|
|
||||||
|
|
||||||
// Send initial player list
|
// Send initial player list
|
||||||
client.send_msg(ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::Init(
|
client.send_msg(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init(
|
||||||
player_list.clone(),
|
player_list.clone(),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
@ -458,7 +454,7 @@ impl Sys {
|
|||||||
alias_validator: &ReadExpect<'_, AliasValidator>,
|
alias_validator: &ReadExpect<'_, AliasValidator>,
|
||||||
) -> Result<(), crate::error::Error> {
|
) -> Result<(), crate::error::Error> {
|
||||||
loop {
|
loop {
|
||||||
let q1 = Client::internal_recv(&client.network_error, &mut client.singleton_stream);
|
let q1 = Client::internal_recv(&client.network_error, &mut client.general_stream);
|
||||||
let q2 = Client::internal_recv(&client.network_error, &mut client.in_game_stream);
|
let q2 = Client::internal_recv(&client.network_error, &mut client.in_game_stream);
|
||||||
let q3 =
|
let q3 =
|
||||||
Client::internal_recv(&client.network_error, &mut client.character_screen_stream);
|
Client::internal_recv(&client.network_error, &mut client.character_screen_stream);
|
||||||
@ -693,7 +689,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
||||||
} else if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 * 0.5 {
|
} 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.
|
// Try pinging the client if the timeout is nearing.
|
||||||
client.send_ping(PingMsg::Ping);
|
client.send_msg(PingMsg::Ping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -702,7 +698,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
for entity in new_players {
|
for entity in new_players {
|
||||||
if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) {
|
if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) {
|
||||||
let msg =
|
let msg =
|
||||||
ServerGeneralMsg::PlayerListUpdate(PlayerListUpdate::Add(*uid, PlayerInfo {
|
ServerGeneral::PlayerListUpdate(PlayerListUpdate::Add(*uid, PlayerInfo {
|
||||||
player_alias: player.alias.clone(),
|
player_alias: player.alias.clone(),
|
||||||
is_online: true,
|
is_online: true,
|
||||||
is_admin: admins.get(entity).is_some(),
|
is_admin: admins.get(entity).is_some(),
|
||||||
|
@ -5,7 +5,7 @@ use super::{
|
|||||||
use crate::client::{self, Client, RegionSubscription};
|
use crate::client::{self, Client, RegionSubscription};
|
||||||
use common::{
|
use common::{
|
||||||
comp::{Ori, Player, Pos, Vel},
|
comp::{Ori, Player, Pos, Vel},
|
||||||
msg::ServerGeneralMsg,
|
msg::ServerGeneral,
|
||||||
region::{region_in_vd, regions_in_vd, Event as RegionEvent, RegionMap},
|
region::{region_in_vd, regions_in_vd, Event as RegionEvent, RegionMap},
|
||||||
span,
|
span,
|
||||||
sync::Uid,
|
sync::Uid,
|
||||||
@ -153,7 +153,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.map(|key| subscription.regions.contains(key))
|
.map(|key| subscription.regions.contains(key))
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
client.send_msg(ServerGeneralMsg::DeleteEntity(uid));
|
client.send_msg(ServerGeneral::DeleteEntity(uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -161,7 +161,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
// Tell client to delete entities in the region
|
// Tell client to delete entities in the region
|
||||||
for (&uid, _) in (&uids, region.entities()).join() {
|
for (&uid, _) in (&uids, region.entities()).join() {
|
||||||
client.send_msg(ServerGeneralMsg::DeleteEntity(uid));
|
client.send_msg(ServerGeneral::DeleteEntity(uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Send deleted entities since they won't be processed for this client in entity
|
// 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()
|
.iter()
|
||||||
.flat_map(|v| v.iter())
|
.flat_map(|v| v.iter())
|
||||||
{
|
{
|
||||||
client.send_msg(ServerGeneralMsg::DeleteEntity(Uid(*uid)));
|
client.send_msg(ServerGeneral::DeleteEntity(Uid(*uid)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
{
|
{
|
||||||
// Send message to create entity and tracked components and physics
|
// Send message to create entity and tracked components and physics
|
||||||
// components
|
// components
|
||||||
client.send_msg(ServerGeneralMsg::CreateEntity(
|
client.send_msg(ServerGeneral::CreateEntity(
|
||||||
tracked_comps.create_entity_package(
|
tracked_comps.create_entity_package(
|
||||||
entity,
|
entity,
|
||||||
Some(*pos),
|
Some(*pos),
|
||||||
@ -249,7 +249,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) {
|
|||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
// Send message to create entity and tracked components and physics components
|
// Send message to create entity and tracked components and physics components
|
||||||
client.send_msg(ServerGeneralMsg::CreateEntity(
|
client.send_msg(ServerGeneral::CreateEntity(
|
||||||
tracked_comps.create_entity_package(
|
tracked_comps.create_entity_package(
|
||||||
entity,
|
entity,
|
||||||
Some(*pos),
|
Some(*pos),
|
||||||
|
@ -4,7 +4,7 @@ use common::{
|
|||||||
comp::{self, bird_medium, Alignment, Player, Pos},
|
comp::{self, bird_medium, Alignment, Player, Pos},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
generation::get_npc_name,
|
generation::get_npc_name,
|
||||||
msg::ServerInGameMsg,
|
msg::ServerInGame,
|
||||||
npc::NPC_NAMES,
|
npc::NPC_NAMES,
|
||||||
span,
|
span,
|
||||||
state::TerrainChanges,
|
state::TerrainChanges,
|
||||||
@ -63,7 +63,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Ok((chunk, supplement)) => (chunk, supplement),
|
Ok((chunk, supplement)) => (chunk, supplement),
|
||||||
Err(Some(entity)) => {
|
Err(Some(entity)) => {
|
||||||
if let Some(client) = clients.get_mut(entity) {
|
if let Some(client) = clients.get_mut(entity) {
|
||||||
client.send_in_game(ServerInGameMsg::TerrainChunkUpdate {
|
client.send_msg(ServerInGame::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
chunk: Err(()),
|
chunk: Err(()),
|
||||||
});
|
});
|
||||||
@ -90,7 +90,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.magnitude_squared();
|
.magnitude_squared();
|
||||||
|
|
||||||
if adjusted_dist_sqr <= view_distance.pow(2) {
|
if adjusted_dist_sqr <= view_distance.pow(2) {
|
||||||
client.send_in_game(ServerInGameMsg::TerrainChunkUpdate {
|
client.send_msg(ServerInGame::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
chunk: Ok(Box::new(chunk.clone())),
|
chunk: Ok(Box::new(chunk.clone())),
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@ use super::SysTimer;
|
|||||||
use crate::client::Client;
|
use crate::client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{Player, Pos},
|
comp::{Player, Pos},
|
||||||
msg::ServerInGameMsg,
|
msg::ServerInGame,
|
||||||
span,
|
span,
|
||||||
state::TerrainChanges,
|
state::TerrainChanges,
|
||||||
terrain::TerrainGrid,
|
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))
|
.map(|vd| super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, vd))
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
client.send_in_game(ServerInGameMsg::TerrainChunkUpdate {
|
client.send_msg(ServerInGame::TerrainChunkUpdate {
|
||||||
key: *chunk_key,
|
key: *chunk_key,
|
||||||
chunk: Ok(Box::new(match terrain.get_key(*chunk_key) {
|
chunk: Ok(Box::new(match terrain.get_key(*chunk_key) {
|
||||||
Some(chunk) => chunk.clone(),
|
Some(chunk) => chunk.clone(),
|
||||||
@ -51,10 +51,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
// TODO: Don't send all changed blocks to all clients
|
// TODO: Don't send all changed blocks to all clients
|
||||||
// Sync changed blocks
|
// Sync changed blocks
|
||||||
let msg = ServerInGameMsg::TerrainBlockUpdates(terrain_changes.modified_blocks.clone());
|
let msg = ServerInGame::TerrainBlockUpdates(terrain_changes.modified_blocks.clone());
|
||||||
for (player, client) in (&players, &mut clients).join() {
|
for (player, client) in (&players, &mut clients).join() {
|
||||||
if player.view_distance.is_some() {
|
if player.view_distance.is_some() {
|
||||||
client.send_in_game(msg.clone());
|
client.send_msg(msg.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use super::SysTimer;
|
|||||||
use crate::client::Client;
|
use crate::client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{Player, Pos, Waypoint, WaypointArea},
|
comp::{Player, Pos, Waypoint, WaypointArea},
|
||||||
msg::{Notification, ServerGeneralMsg},
|
msg::{Notification, ServerGeneral},
|
||||||
span,
|
span,
|
||||||
state::Time,
|
state::Time,
|
||||||
};
|
};
|
||||||
@ -42,9 +42,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
if let Ok(wp_old) = waypoints.insert(entity, Waypoint::new(player_pos.0, *time))
|
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) {
|
if wp_old.map_or(true, |w| w.elapsed(*time) > NOTIFY_TIME) {
|
||||||
client.send_msg(ServerGeneralMsg::Notification(
|
client
|
||||||
Notification::WaypointSaved,
|
.send_msg(ServerGeneral::Notification(Notification::WaypointSaved));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ use crate::{
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{self, bird_medium, quadruped_low, quadruped_medium, quadruped_small},
|
comp::{self, bird_medium, quadruped_low, quadruped_medium, quadruped_small},
|
||||||
generation::{ChunkSupplement, EntityInfo},
|
generation::{ChunkSupplement, EntityInfo},
|
||||||
msg::server::WorldMapMsg,
|
msg::WorldMapMsg,
|
||||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
terrain::{Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||||
vol::{ReadVol, RectVolSize, WriteVol},
|
vol::{ReadVol, RectVolSize, WriteVol},
|
||||||
};
|
};
|
||||||
|
@ -33,7 +33,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets,
|
||||||
msg::server::WorldMapMsg,
|
msg::WorldMapMsg,
|
||||||
store::Id,
|
store::Id,
|
||||||
terrain::{
|
terrain::{
|
||||||
map::MapConfig, uniform_idx_as_vec2, vec2_as_uniform_idx, BiomeKind, MapSizeLg,
|
map::MapConfig, uniform_idx_as_vec2, vec2_as_uniform_idx, BiomeKind, MapSizeLg,
|
||||||
|
Loading…
Reference in New Issue
Block a user