2020-02-16 20:04:06 +00:00
|
|
|
use super::Event;
|
2020-05-13 12:08:26 +00:00
|
|
|
use crate::{
|
2020-07-23 17:16:52 +00:00
|
|
|
client::Client, login_provider::LoginProvider, persistence, state_ext::StateExt, Server,
|
2020-05-13 12:08:26 +00:00
|
|
|
};
|
2020-02-16 20:04:06 +00:00
|
|
|
use common::{
|
|
|
|
comp,
|
2020-04-26 17:03:19 +00:00
|
|
|
comp::{group, Player},
|
2020-10-05 10:44:33 +00:00
|
|
|
msg::{PlayerListUpdate, ServerGeneralMsg, ServerInGameMsg},
|
2020-08-29 06:39:16 +00:00
|
|
|
span,
|
2020-02-16 20:04:06 +00:00
|
|
|
sync::{Uid, UidAllocator},
|
|
|
|
};
|
2020-07-05 23:29:28 +00:00
|
|
|
use futures_executor::block_on;
|
2020-06-05 01:48:26 +00:00
|
|
|
use specs::{saveload::MarkerAllocator, Builder, Entity as EcsEntity, WorldExt};
|
2020-08-17 14:34:02 +00:00
|
|
|
use tracing::{debug, error, trace, warn};
|
2020-02-16 20:04:06 +00:00
|
|
|
|
|
|
|
pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) {
|
2020-08-29 06:39:16 +00:00
|
|
|
span!(_guard, "handle_exit_ingame");
|
2020-02-16 20:04:06 +00:00
|
|
|
let state = server.state_mut();
|
|
|
|
|
|
|
|
// Create new entity with just `Client`, `Uid`, and `Player` components
|
|
|
|
// Easier than checking and removing all other known components
|
|
|
|
// Note: If other `ServerEvent`s are referring to this entity they will be
|
|
|
|
// disrupted
|
|
|
|
let maybe_client = state.ecs().write_storage::<Client>().remove(entity);
|
2020-08-23 20:29:40 +00:00
|
|
|
let maybe_uid = state.read_component_copied::<Uid>(entity);
|
2020-02-16 20:04:06 +00:00
|
|
|
let maybe_player = state.ecs().write_storage::<comp::Player>().remove(entity);
|
2020-09-27 19:25:00 +00:00
|
|
|
let maybe_admin = state.ecs().write_storage::<comp::Admin>().remove(entity);
|
|
|
|
|
2020-04-26 17:03:19 +00:00
|
|
|
let maybe_group = state
|
|
|
|
.ecs()
|
|
|
|
.write_storage::<group::Group>()
|
|
|
|
.get(entity)
|
|
|
|
.cloned();
|
2020-02-16 20:04:06 +00:00
|
|
|
if let (Some(mut client), Some(uid), Some(player)) = (maybe_client, maybe_uid, maybe_player) {
|
|
|
|
// Tell client its request was successful
|
Redo Network Frontend.
Rather than having a single Stream to handle ALL data, seperate into multiple streams:
- Ping Stream, for seperate PINGS
- Register Stream, only used till the client is registered, then no longer used!
- General Stream, used for msg that can occur always
- NotInGame Stream, used for everything NOT ingame, e.g. Character Screen
- InGame Stream, used for all GAME data, players, terrain, entities, etc...
This version does compile, and gets the client registered (with auth too) but doesnt get to the char screen yet.
This fixes also the ignoring messages problem we had, as we are not sending data to the register stream!
This fixes also the problem that the server had to sleep for the Stream Creation, as the Server is now creating the streams and client has to sleep.
2020-10-04 18:20:18 +00:00
|
|
|
client.in_game = None;
|
|
|
|
client.send_in_game(ServerInGameMsg::ExitInGameSuccess);
|
2020-02-16 20:04:06 +00:00
|
|
|
|
|
|
|
let entity_builder = state.ecs_mut().create_entity().with(client).with(player);
|
2020-04-26 17:03:19 +00:00
|
|
|
|
2020-09-27 19:25:00 +00:00
|
|
|
// Preserve group component if present
|
2020-04-26 17:03:19 +00:00
|
|
|
let entity_builder = match maybe_group {
|
|
|
|
Some(group) => entity_builder.with(group),
|
|
|
|
None => entity_builder,
|
|
|
|
};
|
|
|
|
|
2020-09-27 19:25:00 +00:00
|
|
|
// Preserve admin component if present
|
|
|
|
let entity_builder = match maybe_admin {
|
|
|
|
Some(admin) => entity_builder.with(admin),
|
|
|
|
None => entity_builder,
|
|
|
|
};
|
|
|
|
|
2020-02-16 20:04:06 +00:00
|
|
|
// Ensure UidAllocator maps this uid to the new entity
|
|
|
|
let uid = entity_builder
|
|
|
|
.world
|
|
|
|
.write_resource::<UidAllocator>()
|
|
|
|
.allocate(entity_builder.entity, Some(uid.into()));
|
2020-04-26 17:03:19 +00:00
|
|
|
let new_entity = entity_builder.with(uid).build();
|
|
|
|
if let Some(group) = maybe_group {
|
|
|
|
let mut group_manager = state.ecs().write_resource::<group::GroupManager>();
|
|
|
|
if group_manager
|
|
|
|
.group_info(group)
|
|
|
|
.map(|info| info.leader == entity)
|
|
|
|
.unwrap_or(false)
|
|
|
|
{
|
|
|
|
group_manager.assign_leader(
|
|
|
|
new_entity,
|
|
|
|
&state.ecs().read_storage(),
|
|
|
|
&state.ecs().entities(),
|
2020-07-19 21:49:18 +00:00
|
|
|
&state.ecs().read_storage(),
|
|
|
|
&state.ecs().read_storage(),
|
2020-08-07 01:59:28 +00:00
|
|
|
// Nothing actually changing since Uid is transferred
|
2020-04-26 17:03:19 +00:00
|
|
|
|_, _| {},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-02-16 20:04:06 +00:00
|
|
|
}
|
2020-04-26 17:03:19 +00:00
|
|
|
// Erase group component to avoid group restructure when deleting the entity
|
|
|
|
state.ecs().write_storage::<group::Group>().remove(entity);
|
2020-02-16 20:04:06 +00:00
|
|
|
// Delete old entity
|
2020-06-21 21:47:49 +00:00
|
|
|
if let Err(e) = state.delete_entity_recorded(entity) {
|
|
|
|
error!(
|
|
|
|
?e,
|
|
|
|
?entity,
|
|
|
|
"Failed to delete entity when removing character"
|
|
|
|
);
|
2020-02-16 20:04:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event {
|
2020-08-29 06:39:16 +00:00
|
|
|
span!(_guard, "handle_client_disconnect");
|
2020-07-05 23:29:28 +00:00
|
|
|
if let Some(client) = server.state().read_storage::<Client>().get(entity) {
|
2020-08-17 12:21:36 +00:00
|
|
|
let participant = match client.participant.try_lock() {
|
|
|
|
Ok(mut p) => p.take().unwrap(),
|
|
|
|
Err(e) => {
|
2020-08-25 12:21:25 +00:00
|
|
|
error!(?e, ?entity, "couldn't lock participant for removal");
|
2020-08-17 14:34:02 +00:00
|
|
|
return Event::ClientDisconnected { entity };
|
2020-08-17 12:21:36 +00:00
|
|
|
},
|
|
|
|
};
|
2020-08-18 16:42:02 +00:00
|
|
|
let pid = participant.remote_pid();
|
|
|
|
std::thread::spawn(move || {
|
|
|
|
let span = tracing::span!(tracing::Level::DEBUG, "client_disconnect", ?pid, ?entity);
|
|
|
|
let _enter = span.enter();
|
2020-08-17 14:34:02 +00:00
|
|
|
let now = std::time::Instant::now();
|
2020-08-18 16:42:02 +00:00
|
|
|
debug!(?pid, ?entity, "Start handle disconnect of client");
|
2020-08-17 14:34:02 +00:00
|
|
|
if let Err(e) = block_on(participant.disconnect()) {
|
|
|
|
debug!(
|
|
|
|
?e,
|
2020-08-18 11:48:26 +00:00
|
|
|
?pid,
|
2020-08-17 14:34:02 +00:00
|
|
|
"Error when disconnecting client, maybe the pipe already broke"
|
|
|
|
);
|
|
|
|
};
|
|
|
|
trace!(?pid, "finished disconnect");
|
|
|
|
let elapsed = now.elapsed();
|
|
|
|
if elapsed.as_millis() > 100 {
|
2020-08-25 12:21:25 +00:00
|
|
|
warn!(?elapsed, ?pid, "disconnecting took quite long");
|
2020-08-17 14:34:02 +00:00
|
|
|
} else {
|
2020-08-25 12:21:25 +00:00
|
|
|
debug!(?elapsed, ?pid, "disconnecting took");
|
2020-08-17 14:34:02 +00:00
|
|
|
}
|
|
|
|
});
|
2020-07-05 23:29:28 +00:00
|
|
|
}
|
|
|
|
|
2020-02-16 20:04:06 +00:00
|
|
|
let state = server.state_mut();
|
|
|
|
|
|
|
|
// Tell other clients to remove from player list
|
2020-09-06 19:42:32 +00:00
|
|
|
// And send a disconnected message
|
2020-02-16 20:04:06 +00:00
|
|
|
if let (Some(uid), Some(_)) = (
|
|
|
|
state.read_storage::<Uid>().get(entity),
|
|
|
|
state.read_storage::<comp::Player>().get(entity),
|
|
|
|
) {
|
2020-09-06 19:42:32 +00:00
|
|
|
state.notify_registered_clients(comp::ChatType::Offline(*uid).server_msg(""));
|
|
|
|
|
2020-10-05 10:44:33 +00:00
|
|
|
state.notify_registered_clients(ServerGeneralMsg::PlayerListUpdate(
|
|
|
|
PlayerListUpdate::Remove(*uid),
|
|
|
|
));
|
2020-02-16 20:04:06 +00:00
|
|
|
}
|
|
|
|
|
2020-07-23 17:16:52 +00:00
|
|
|
// Make sure to remove the player from the logged in list. (See LoginProvider)
|
2020-06-05 01:48:26 +00:00
|
|
|
if let Some(player) = state.ecs().read_storage::<Player>().get(entity) {
|
2020-07-23 17:16:52 +00:00
|
|
|
let mut login_provider = state.ecs().write_resource::<LoginProvider>();
|
|
|
|
login_provider.logout(player.uuid());
|
2020-03-09 19:52:04 +00:00
|
|
|
}
|
2020-05-13 12:08:26 +00:00
|
|
|
|
|
|
|
// Sync the player's character data to the database
|
2020-06-04 11:44:33 +00:00
|
|
|
if let (Some(player), Some(stats), Some(inventory), Some(loadout), updater) = (
|
2020-05-13 12:08:26 +00:00
|
|
|
state.read_storage::<Player>().get(entity),
|
|
|
|
state.read_storage::<comp::Stats>().get(entity),
|
2020-06-01 21:34:52 +00:00
|
|
|
state.read_storage::<comp::Inventory>().get(entity),
|
2020-06-04 11:44:33 +00:00
|
|
|
state.read_storage::<comp::Loadout>().get(entity),
|
2020-06-01 21:34:52 +00:00
|
|
|
state
|
|
|
|
.ecs()
|
2020-09-17 23:02:14 +00:00
|
|
|
.read_resource::<persistence::character_updater::CharacterUpdater>(),
|
2020-05-13 12:08:26 +00:00
|
|
|
) {
|
|
|
|
if let Some(character_id) = player.character_id {
|
2020-06-04 11:44:33 +00:00
|
|
|
updater.update(character_id, stats, inventory, loadout);
|
2020-05-13 12:08:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-16 20:04:06 +00:00
|
|
|
// Delete client entity
|
2020-06-21 21:47:49 +00:00
|
|
|
if let Err(e) = state.delete_entity_recorded(entity) {
|
|
|
|
error!(?e, ?entity, "Failed to delete disconnected client");
|
2020-02-16 20:04:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Event::ClientDisconnected { entity }
|
|
|
|
}
|