2019-10-20 07:20:21 +00:00
|
|
|
use super::SysTimer;
|
2020-05-12 23:58:15 +00:00
|
|
|
use crate::{
|
2020-06-16 01:00:32 +00:00
|
|
|
auth_provider::AuthProvider, client::Client, persistence::character::CharacterLoader,
|
2020-06-25 11:20:09 +00:00
|
|
|
ServerSettings,
|
2020-05-12 23:58:15 +00:00
|
|
|
CLIENT_TIMEOUT,
|
|
|
|
};
|
2019-10-15 04:06:14 +00:00
|
|
|
use common::{
|
2020-05-24 22:18:41 +00:00
|
|
|
comp::{
|
|
|
|
Admin, CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, SpeechBubble,
|
2020-05-26 00:11:22 +00:00
|
|
|
Stats, Vel,
|
2020-05-24 22:18:41 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
event::{EventBus, ServerEvent},
|
2019-12-23 06:02:00 +00:00
|
|
|
msg::{
|
2020-05-24 09:20:54 +00:00
|
|
|
validate_chat_msg, CharacterInfo, ChatMsgValidationError, ClientMsg, ClientState,
|
|
|
|
PlayerInfo, PlayerListUpdate, RequestStateError, ServerMsg, MAX_BYTES_CHAT_MSG,
|
2019-12-23 06:02:00 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
state::{BlockChange, Time},
|
2019-12-23 06:02:00 +00:00
|
|
|
sync::Uid,
|
2020-06-25 12:07:01 +00:00
|
|
|
terrain::{Block, TerrainChunkSize, TerrainGrid},
|
|
|
|
vol::{RectVolSize, Vox},
|
2019-10-15 04:06:14 +00:00
|
|
|
};
|
2019-12-23 06:02:00 +00:00
|
|
|
use hashbrown::HashMap;
|
2019-10-15 04:06:14 +00:00
|
|
|
use specs::{
|
|
|
|
Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage,
|
|
|
|
};
|
2020-06-21 14:26:06 +00:00
|
|
|
use tracing::warn;
|
2019-10-15 04:06:14 +00:00
|
|
|
|
|
|
|
/// This system will handle new messages from clients
|
|
|
|
pub struct Sys;
|
|
|
|
impl<'a> System<'a> for Sys {
|
2020-06-10 19:47:36 +00:00
|
|
|
#[allow(clippy::type_complexity)] // TODO: Pending review in #587
|
2019-10-15 04:06:14 +00:00
|
|
|
type SystemData = (
|
|
|
|
Entities<'a>,
|
|
|
|
Read<'a, EventBus<ServerEvent>>,
|
|
|
|
Read<'a, Time>,
|
2020-06-16 01:00:32 +00:00
|
|
|
ReadExpect<'a, CharacterLoader>,
|
2019-10-15 04:06:14 +00:00
|
|
|
ReadExpect<'a, TerrainGrid>,
|
2019-10-20 07:20:21 +00:00
|
|
|
Write<'a, SysTimer<Self>>,
|
2019-12-23 06:02:00 +00:00
|
|
|
ReadStorage<'a, Uid>,
|
2019-10-15 04:06:14 +00:00
|
|
|
ReadStorage<'a, CanBuild>,
|
|
|
|
ReadStorage<'a, Admin>,
|
2019-11-29 06:04:37 +00:00
|
|
|
ReadStorage<'a, ForceUpdate>,
|
2019-12-31 08:10:51 +00:00
|
|
|
ReadStorage<'a, Stats>,
|
2019-10-15 04:06:14 +00:00
|
|
|
WriteExpect<'a, AuthProvider>,
|
|
|
|
Write<'a, BlockChange>,
|
|
|
|
WriteStorage<'a, Pos>,
|
|
|
|
WriteStorage<'a, Vel>,
|
|
|
|
WriteStorage<'a, Ori>,
|
|
|
|
WriteStorage<'a, Player>,
|
|
|
|
WriteStorage<'a, Client>,
|
|
|
|
WriteStorage<'a, Controller>,
|
2020-05-24 22:18:41 +00:00
|
|
|
WriteStorage<'a, SpeechBubble>,
|
2020-06-25 11:20:09 +00:00
|
|
|
Read<'a, ServerSettings>,
|
2019-10-15 04:06:14 +00:00
|
|
|
);
|
|
|
|
|
2020-06-10 19:47:36 +00:00
|
|
|
#[allow(clippy::match_ref_pats)] // TODO: Pending review in #587
|
|
|
|
#[allow(clippy::single_char_pattern)] // TODO: Pending review in #587
|
|
|
|
#[allow(clippy::single_match)] // TODO: Pending review in #587
|
2019-10-15 04:06:14 +00:00
|
|
|
fn run(
|
|
|
|
&mut self,
|
|
|
|
(
|
|
|
|
entities,
|
2020-03-22 04:49:32 +00:00
|
|
|
server_event_bus,
|
2019-10-15 04:06:14 +00:00
|
|
|
time,
|
2020-06-16 01:00:32 +00:00
|
|
|
character_loader,
|
2019-10-15 04:06:14 +00:00
|
|
|
terrain,
|
2019-10-20 07:20:21 +00:00
|
|
|
mut timer,
|
2019-12-23 06:02:00 +00:00
|
|
|
uids,
|
2019-10-15 04:06:14 +00:00
|
|
|
can_build,
|
|
|
|
admins,
|
2019-11-29 06:04:37 +00:00
|
|
|
force_updates,
|
2019-12-31 08:10:51 +00:00
|
|
|
stats,
|
2019-10-15 04:06:14 +00:00
|
|
|
mut accounts,
|
|
|
|
mut block_changes,
|
|
|
|
mut positions,
|
|
|
|
mut velocities,
|
|
|
|
mut orientations,
|
|
|
|
mut players,
|
|
|
|
mut clients,
|
|
|
|
mut controllers,
|
2020-05-24 22:18:41 +00:00
|
|
|
mut speech_bubbles,
|
2020-06-25 11:20:09 +00:00
|
|
|
settings,
|
2019-10-15 04:06:14 +00:00
|
|
|
): Self::SystemData,
|
|
|
|
) {
|
2019-10-20 07:20:21 +00:00
|
|
|
timer.start();
|
|
|
|
|
2020-03-22 04:49:32 +00:00
|
|
|
let mut server_emitter = server_event_bus.emitter();
|
|
|
|
|
2019-10-15 04:06:14 +00:00
|
|
|
let mut new_chat_msgs = Vec::new();
|
|
|
|
|
2019-12-23 06:02:00 +00:00
|
|
|
// Player list to send new players.
|
2020-05-20 11:59:44 +00:00
|
|
|
let player_list = (&uids, &players, &stats)
|
2019-12-23 06:02:00 +00:00
|
|
|
.join()
|
2020-05-20 11:59:44 +00:00
|
|
|
.map(|(uid, player, stats)| {
|
|
|
|
((*uid).into(), PlayerInfo {
|
|
|
|
player_alias: player.alias.clone(),
|
2020-05-24 09:20:54 +00:00
|
|
|
// TODO: player might not have a character selected
|
|
|
|
character: Some(CharacterInfo {
|
|
|
|
name: stats.name.clone(),
|
|
|
|
level: stats.level.level(),
|
|
|
|
}),
|
2020-05-20 11:59:44 +00:00
|
|
|
})
|
|
|
|
})
|
2019-12-23 06:02:00 +00:00
|
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
// List of new players to update player lists of all clients.
|
|
|
|
let mut new_players = Vec::new();
|
|
|
|
|
2019-10-15 04:06:14 +00:00
|
|
|
for (entity, client) in (&entities, &mut clients).join() {
|
|
|
|
let new_msgs = client.postbox.new_messages();
|
|
|
|
|
|
|
|
// Update client ping.
|
|
|
|
if new_msgs.len() > 0 {
|
2020-05-26 00:11:22 +00:00
|
|
|
client.last_ping = time.0
|
|
|
|
} else if time.0 - client.last_ping > CLIENT_TIMEOUT // Timeout
|
2019-10-15 04:06:14 +00:00
|
|
|
|| client.postbox.error().is_some()
|
|
|
|
// Postbox error
|
|
|
|
{
|
2020-03-08 08:54:29 +00:00
|
|
|
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
2020-05-26 00:11:22 +00:00
|
|
|
} else if time.0 - client.last_ping > CLIENT_TIMEOUT * 0.5 {
|
2019-10-15 04:06:14 +00:00
|
|
|
// Try pinging the client if the timeout is nearing.
|
|
|
|
client.postbox.send_message(ServerMsg::Ping);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process incoming messages.
|
|
|
|
for msg in new_msgs {
|
|
|
|
match msg {
|
2019-12-31 08:10:51 +00:00
|
|
|
// Go back to registered state (char selection screen)
|
|
|
|
ClientMsg::ExitIngame => match client.client_state {
|
|
|
|
// Use ClientMsg::Register instead.
|
|
|
|
ClientState::Connected => {
|
2019-10-15 04:06:14 +00:00
|
|
|
client.error_state(RequestStateError::WrongMessage)
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-12-31 08:10:51 +00:00
|
|
|
ClientState::Registered => client.error_state(RequestStateError::Already),
|
|
|
|
ClientState::Spectator | ClientState::Character => {
|
|
|
|
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
|
|
|
ClientState::Pending => {},
|
2019-12-31 08:10:51 +00:00
|
|
|
},
|
|
|
|
// Request spectator state
|
|
|
|
ClientMsg::Spectate => match client.client_state {
|
|
|
|
// Become Registered first.
|
|
|
|
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
|
|
|
ClientState::Spectator => client.error_state(RequestStateError::Already),
|
|
|
|
ClientState::Registered | ClientState::Character => {
|
|
|
|
client.allow_state(ClientState::Spectator)
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
|
|
|
ClientState::Pending => {},
|
2019-10-15 04:06:14 +00:00
|
|
|
},
|
2020-01-11 21:04:49 +00:00
|
|
|
// Request registered state (login)
|
2020-01-02 08:43:45 +00:00
|
|
|
ClientMsg::Register {
|
2020-01-11 21:04:49 +00:00
|
|
|
view_distance,
|
2020-01-02 08:43:45 +00:00
|
|
|
token_or_username,
|
2020-01-11 21:04:49 +00:00
|
|
|
} => {
|
|
|
|
let (username, uuid) = match accounts.query(token_or_username.clone()) {
|
2020-01-07 06:27:18 +00:00
|
|
|
Err(err) => {
|
|
|
|
client.error_state(RequestStateError::RegisterDenied(err));
|
|
|
|
break;
|
|
|
|
},
|
2020-01-11 21:04:49 +00:00
|
|
|
Ok((username, uuid)) => (username, uuid),
|
|
|
|
};
|
|
|
|
|
2020-06-25 12:07:01 +00:00
|
|
|
let vd = view_distance
|
|
|
|
.map(|vd| vd.min(settings.max_view_distance.unwrap_or(vd)));
|
2020-06-25 11:20:09 +00:00
|
|
|
let player = Player::new(username, None, vd, uuid);
|
2020-01-11 21:04:49 +00:00
|
|
|
|
|
|
|
if !player.is_valid() {
|
|
|
|
// Invalid player
|
|
|
|
client.error_state(RequestStateError::Impossible);
|
|
|
|
break;
|
2019-10-15 04:06:14 +00:00
|
|
|
}
|
2020-01-11 21:04:49 +00:00
|
|
|
|
2019-10-15 04:06:14 +00:00
|
|
|
match client.client_state {
|
|
|
|
ClientState::Connected => {
|
2019-12-23 06:02:00 +00:00
|
|
|
// Add Player component to this client
|
2019-10-15 04:06:14 +00:00
|
|
|
let _ = players.insert(entity, player);
|
|
|
|
|
|
|
|
// Tell the client its request was successful.
|
|
|
|
client.allow_state(ClientState::Registered);
|
2019-12-23 06:02:00 +00:00
|
|
|
|
|
|
|
// Send initial player list
|
|
|
|
client.notify(ServerMsg::PlayerListUpdate(PlayerListUpdate::Init(
|
|
|
|
player_list.clone(),
|
|
|
|
)));
|
2020-05-09 15:41:25 +00:00
|
|
|
|
2019-12-23 06:02:00 +00:00
|
|
|
// Add to list to notify all clients of the new player
|
|
|
|
new_players.push(entity);
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
// Use RequestState instead (No need to send `player` again).
|
|
|
|
_ => client.error_state(RequestStateError::Impossible),
|
|
|
|
}
|
|
|
|
//client.allow_state(ClientState::Registered);
|
2020-06-25 11:20:09 +00:00
|
|
|
|
|
|
|
// Limit view distance if it's too high
|
|
|
|
// This comes after state registration so that the client actually hears it
|
2020-06-25 12:07:01 +00:00
|
|
|
if settings
|
|
|
|
.max_view_distance
|
|
|
|
.zip(view_distance)
|
|
|
|
.map(|(vd, max)| vd > max)
|
|
|
|
.unwrap_or(false)
|
|
|
|
{
|
|
|
|
client.notify(ServerMsg::SetViewDistance(
|
|
|
|
settings.max_view_distance.unwrap_or(0),
|
|
|
|
));
|
2020-06-25 11:20:09 +00:00
|
|
|
};
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
ClientMsg::SetViewDistance(view_distance) => match client.client_state {
|
|
|
|
ClientState::Character { .. } => {
|
2020-06-25 12:07:01 +00:00
|
|
|
if settings
|
|
|
|
.max_view_distance
|
|
|
|
.map(|max| view_distance <= max)
|
|
|
|
.unwrap_or(true)
|
|
|
|
{
|
|
|
|
players.get_mut(entity).map(|player| {
|
|
|
|
player.view_distance = Some(
|
|
|
|
settings
|
|
|
|
.max_view_distance
|
2020-06-25 11:20:09 +00:00
|
|
|
.map(|max| view_distance.min(max))
|
2020-06-25 12:07:01 +00:00
|
|
|
.unwrap_or(view_distance),
|
|
|
|
)
|
|
|
|
});
|
2020-06-25 11:20:09 +00:00
|
|
|
} else {
|
2020-06-25 12:07:01 +00:00
|
|
|
client.notify(ServerMsg::SetViewDistance(
|
|
|
|
settings.max_view_distance.unwrap_or(0),
|
|
|
|
));
|
2020-06-25 11:20:09 +00:00
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
|
|
|
_ => {},
|
2019-10-15 04:06:14 +00:00
|
|
|
},
|
2020-06-16 01:00:32 +00:00
|
|
|
ClientMsg::Character(character_id) => match client.client_state {
|
2019-10-15 04:06:14 +00:00
|
|
|
// Become Registered first.
|
|
|
|
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
2019-12-31 08:10:51 +00:00
|
|
|
ClientState::Registered | ClientState::Spectator => {
|
2020-06-16 01:00:32 +00:00
|
|
|
if let Some(player) = players.get(entity) {
|
|
|
|
// Send a request to load the character's component data from the
|
|
|
|
// DB. Once loaded, persisted components such as stats and inventory
|
|
|
|
// will be inserted for the entity
|
|
|
|
character_loader.load_character_data(
|
|
|
|
entity,
|
|
|
|
player.uuid().to_string(),
|
|
|
|
character_id,
|
|
|
|
);
|
2020-05-09 15:41:25 +00:00
|
|
|
|
2020-06-16 01:00:32 +00:00
|
|
|
// Start inserting non-persisted/default components for the entity
|
|
|
|
// while we load the DB data
|
|
|
|
server_emitter.emit(ServerEvent::InitCharacterData {
|
|
|
|
entity,
|
|
|
|
character_id,
|
|
|
|
});
|
2019-10-15 04:06:14 +00:00
|
|
|
|
2020-06-25 12:07:01 +00:00
|
|
|
// Give the player a welcome message
|
|
|
|
if settings.server_description.len() > 0 {
|
|
|
|
client.notify(ServerMsg::broadcast(format!(
|
|
|
|
"{}",
|
|
|
|
settings.server_description
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
2020-06-16 01:00:32 +00:00
|
|
|
// Only send login message if it wasn't already
|
|
|
|
// sent previously
|
|
|
|
if !client.login_msg_sent {
|
|
|
|
new_chat_msgs.push((
|
|
|
|
None,
|
|
|
|
ServerMsg::broadcast(format!(
|
|
|
|
"[{}] is now online.",
|
|
|
|
&player.alias
|
|
|
|
)),
|
|
|
|
));
|
|
|
|
|
|
|
|
client.login_msg_sent = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
client.notify(ServerMsg::CharacterDataLoadError(String::from(
|
|
|
|
"Failed to fetch player entity",
|
|
|
|
)))
|
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
ClientState::Character => client.error_state(RequestStateError::Already),
|
2020-02-01 20:39:39 +00:00
|
|
|
ClientState::Pending => {},
|
2019-10-15 04:06:14 +00:00
|
|
|
},
|
|
|
|
ClientMsg::ControllerInputs(inputs) => match client.client_state {
|
|
|
|
ClientState::Connected
|
|
|
|
| ClientState::Registered
|
|
|
|
| ClientState::Spectator => {
|
|
|
|
client.error_state(RequestStateError::Impossible)
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-12-31 08:10:51 +00:00
|
|
|
ClientState::Character => {
|
2019-10-15 04:06:14 +00:00
|
|
|
if let Some(controller) = controllers.get_mut(entity) {
|
2020-03-24 07:38:16 +00:00
|
|
|
controller.inputs.update_with_new(inputs);
|
2019-10-15 04:06:14 +00:00
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
|
|
|
ClientState::Pending => {},
|
2019-10-15 04:06:14 +00:00
|
|
|
},
|
|
|
|
ClientMsg::ControlEvent(event) => match client.client_state {
|
|
|
|
ClientState::Connected
|
|
|
|
| ClientState::Registered
|
|
|
|
| ClientState::Spectator => {
|
|
|
|
client.error_state(RequestStateError::Impossible)
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-12-31 08:10:51 +00:00
|
|
|
ClientState::Character => {
|
2020-03-24 07:38:16 +00:00
|
|
|
// Skip respawn if client entity is alive
|
|
|
|
if let &ControlEvent::Respawn = &event {
|
|
|
|
if stats.get(entity).map_or(true, |s| !s.is_dead) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2019-10-15 04:06:14 +00:00
|
|
|
if let Some(controller) = controllers.get_mut(entity) {
|
|
|
|
controller.events.push(event);
|
|
|
|
}
|
2020-03-24 07:38:16 +00:00
|
|
|
},
|
|
|
|
ClientState::Pending => {},
|
|
|
|
},
|
|
|
|
ClientMsg::ControlAction(event) => match client.client_state {
|
|
|
|
ClientState::Connected
|
|
|
|
| ClientState::Registered
|
|
|
|
| ClientState::Spectator => {
|
|
|
|
client.error_state(RequestStateError::Impossible)
|
|
|
|
},
|
|
|
|
ClientState::Character => {
|
|
|
|
if let Some(controller) = controllers.get_mut(entity) {
|
|
|
|
controller.actions.push(event);
|
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
|
|
|
ClientState::Pending => {},
|
2019-10-15 04:06:14 +00:00
|
|
|
},
|
2019-12-31 08:10:51 +00:00
|
|
|
ClientMsg::ChatMsg { message } => match client.client_state {
|
2019-10-15 04:06:14 +00:00
|
|
|
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
|
|
|
ClientState::Registered
|
|
|
|
| ClientState::Spectator
|
|
|
|
| ClientState::Character => match validate_chat_msg(&message) {
|
2019-12-31 08:10:51 +00:00
|
|
|
Ok(()) => new_chat_msgs.push((Some(entity), ServerMsg::chat(message))),
|
2020-06-21 21:47:49 +00:00
|
|
|
Err(ChatMsgValidationError::TooLong) => {
|
|
|
|
let max = MAX_BYTES_CHAT_MSG;
|
|
|
|
let len = message.len();
|
|
|
|
warn!(?len, ?max, "Recieved a chat message that's too long")
|
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
},
|
2020-02-01 20:39:39 +00:00
|
|
|
ClientState::Pending => {},
|
2019-10-15 04:06:14 +00:00
|
|
|
},
|
|
|
|
ClientMsg::PlayerPhysics { pos, vel, ori } => match client.client_state {
|
|
|
|
ClientState::Character => {
|
2019-12-31 08:10:51 +00:00
|
|
|
if force_updates.get(entity).is_none()
|
|
|
|
&& stats.get(entity).map_or(true, |s| !s.is_dead)
|
|
|
|
{
|
2019-11-29 06:04:37 +00:00
|
|
|
let _ = positions.insert(entity, pos);
|
|
|
|
let _ = velocities.insert(entity, vel);
|
|
|
|
let _ = orientations.insert(entity, ori);
|
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
// Only characters can send positions.
|
|
|
|
_ => client.error_state(RequestStateError::Impossible),
|
|
|
|
},
|
|
|
|
ClientMsg::BreakBlock(pos) => {
|
|
|
|
if can_build.get(entity).is_some() {
|
|
|
|
block_changes.set(pos, Block::empty());
|
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
ClientMsg::PlaceBlock(pos, block) => {
|
|
|
|
if can_build.get(entity).is_some() {
|
|
|
|
block_changes.try_set(pos, block);
|
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
ClientMsg::TerrainChunkRequest { key } => match client.client_state {
|
2019-12-31 08:10:51 +00:00
|
|
|
ClientState::Connected | ClientState::Registered => {
|
2019-10-15 04:06:14 +00:00
|
|
|
client.error_state(RequestStateError::Impossible);
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
ClientState::Spectator | ClientState::Character => {
|
2020-06-25 11:20:09 +00:00
|
|
|
let in_vd = if let (Some(view_distance), Some(pos)) = (
|
|
|
|
players.get(entity).and_then(|p| p.view_distance),
|
|
|
|
positions.get(entity),
|
|
|
|
) {
|
2020-06-25 12:07:01 +00:00
|
|
|
pos.0.xy().map(|e| e as f64).distance(
|
|
|
|
key.map(|e| e as f64 + 0.5)
|
|
|
|
* TerrainChunkSize::RECT_SIZE.map(|e| e as f64),
|
|
|
|
) < (view_distance as f64 + 1.5)
|
|
|
|
* TerrainChunkSize::RECT_SIZE.x as f64
|
2020-06-25 11:20:09 +00:00
|
|
|
} else {
|
|
|
|
true
|
|
|
|
};
|
|
|
|
if in_vd {
|
|
|
|
match terrain.get_key(key) {
|
|
|
|
Some(chunk) => {
|
|
|
|
client.postbox.send_message(ServerMsg::TerrainChunkUpdate {
|
|
|
|
key,
|
|
|
|
chunk: Ok(Box::new(chunk.clone())),
|
|
|
|
})
|
|
|
|
},
|
2020-06-25 12:07:01 +00:00
|
|
|
None => {
|
|
|
|
server_emitter.emit(ServerEvent::ChunkRequest(entity, key))
|
|
|
|
},
|
2020-06-25 11:20:09 +00:00
|
|
|
}
|
2019-10-15 04:06:14 +00:00
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
|
|
|
ClientState::Pending => {},
|
2019-10-15 04:06:14 +00:00
|
|
|
},
|
|
|
|
// Always possible.
|
|
|
|
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
|
2020-02-01 20:39:39 +00:00
|
|
|
ClientMsg::Pong => {},
|
2019-10-15 04:06:14 +00:00
|
|
|
ClientMsg::Disconnect => {
|
2020-03-10 21:04:13 +00:00
|
|
|
client.postbox.send_message(ServerMsg::Disconnect);
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2020-03-08 08:06:22 +00:00
|
|
|
ClientMsg::Terminate => {
|
|
|
|
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
|
|
|
},
|
2020-05-09 15:41:25 +00:00
|
|
|
ClientMsg::RequestCharacterList => {
|
|
|
|
if let Some(player) = players.get(entity) {
|
2020-06-16 01:00:32 +00:00
|
|
|
character_loader.load_character_list(entity, player.uuid().to_string())
|
2020-05-09 15:41:25 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
ClientMsg::CreateCharacter { alias, tool, body } => {
|
|
|
|
if let Some(player) = players.get(entity) {
|
2020-06-16 01:00:32 +00:00
|
|
|
character_loader.create_character(
|
2020-06-02 08:16:23 +00:00
|
|
|
entity,
|
|
|
|
player.uuid().to_string(),
|
2020-05-09 15:41:25 +00:00
|
|
|
alias,
|
|
|
|
tool,
|
2020-06-02 08:16:23 +00:00
|
|
|
body,
|
|
|
|
);
|
2020-05-09 15:41:25 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
ClientMsg::DeleteCharacter(character_id) => {
|
|
|
|
if let Some(player) = players.get(entity) {
|
2020-06-16 01:00:32 +00:00
|
|
|
character_loader.delete_character(
|
2020-06-02 08:16:23 +00:00
|
|
|
entity,
|
|
|
|
player.uuid().to_string(),
|
2020-05-09 15:41:25 +00:00
|
|
|
character_id,
|
2020-06-02 08:16:23 +00:00
|
|
|
);
|
2020-05-09 15:41:25 +00:00
|
|
|
}
|
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-23 06:02:00 +00:00
|
|
|
// Handle new players.
|
|
|
|
// Tell all clients to add them to the player list.
|
|
|
|
for entity in new_players {
|
2020-05-24 09:20:54 +00:00
|
|
|
if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) {
|
2020-05-20 11:59:44 +00:00
|
|
|
let msg =
|
|
|
|
ServerMsg::PlayerListUpdate(PlayerListUpdate::Add((*uid).into(), PlayerInfo {
|
|
|
|
player_alias: player.alias.clone(),
|
2020-05-24 09:20:54 +00:00
|
|
|
character: None, // new players will be on character select.
|
2020-05-20 11:59:44 +00:00
|
|
|
}));
|
2019-12-23 06:02:00 +00:00
|
|
|
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
|
|
|
client.notify(msg.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-15 04:06:14 +00:00
|
|
|
// Handle new chat messages.
|
|
|
|
for (entity, msg) in new_chat_msgs {
|
|
|
|
match msg {
|
|
|
|
ServerMsg::ChatMsg { chat_type, message } => {
|
2020-05-24 22:18:41 +00:00
|
|
|
let message = if let Some(entity) = entity {
|
2019-10-15 04:06:14 +00:00
|
|
|
// Handle chat commands.
|
|
|
|
if message.starts_with("/") && message.len() > 1 {
|
|
|
|
let argv = String::from(&message[1..]);
|
|
|
|
server_emitter.emit(ServerEvent::ChatCmd(entity, argv));
|
2020-05-24 22:18:41 +00:00
|
|
|
continue;
|
2019-10-15 04:06:14 +00:00
|
|
|
} else {
|
2020-05-26 00:11:22 +00:00
|
|
|
let bubble = SpeechBubble::player_new(message.clone(), *time);
|
2020-05-24 22:18:41 +00:00
|
|
|
let _ = speech_bubbles.insert(entity, bubble);
|
2020-05-20 03:37:14 +00:00
|
|
|
format!(
|
|
|
|
"{}[{}] {}: {}",
|
|
|
|
match admins.get(entity) {
|
|
|
|
Some(_) => "[ADMIN]",
|
|
|
|
None => "",
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2020-05-20 03:37:14 +00:00
|
|
|
match players.get(entity) {
|
|
|
|
Some(player) => &player.alias,
|
|
|
|
None => "<Unknown>",
|
|
|
|
},
|
|
|
|
match stats.get(entity) {
|
|
|
|
Some(stat) => &stat.name,
|
|
|
|
None => "<Unknown>",
|
|
|
|
},
|
|
|
|
message
|
|
|
|
)
|
2019-10-15 04:06:14 +00:00
|
|
|
}
|
|
|
|
} else {
|
2020-05-24 22:18:41 +00:00
|
|
|
message
|
|
|
|
};
|
|
|
|
let msg = ServerMsg::ChatMsg { chat_type, message };
|
|
|
|
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
|
|
|
client.notify(msg.clone());
|
2019-10-15 04:06:14 +00:00
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
_ => {
|
|
|
|
panic!("Invalid message type.");
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-10-15 04:06:14 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-20 07:20:21 +00:00
|
|
|
|
|
|
|
timer.end()
|
2019-10-15 04:06:14 +00:00
|
|
|
}
|
|
|
|
}
|