mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'heydabop/962-duplicate-login' into 'master'
Fix #962 - kick old client and add new client on duplicate login Closes #962 See merge request veloren/veloren!1952
This commit is contained in:
commit
a71684a003
@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Server kicks old client when a user is trying to log in again (often the case when a user's original connection gets dropped)
|
||||
|
||||
## [0.9.0] - 2021-03-20
|
||||
|
||||
### Added
|
||||
|
@ -538,7 +538,7 @@ impl Client {
|
||||
self.send_msg_err(ClientRegister { token_or_username })?;
|
||||
|
||||
match self.register_stream.recv::<ServerRegisterAnswer>().await? {
|
||||
Err(RegisterError::AlreadyLoggedIn) => Err(Error::AlreadyLoggedIn),
|
||||
Err(RegisterError::AlreadyLoggedIn(_, _)) => Err(Error::AlreadyLoggedIn),
|
||||
Err(RegisterError::AuthError(err)) => Err(Error::AuthErr(err)),
|
||||
Err(RegisterError::InvalidCharacter) => Err(Error::InvalidCharacter),
|
||||
Err(RegisterError::NotOnWhitelist) => Err(Error::NotOnWhitelist),
|
||||
|
@ -10,6 +10,7 @@ use common::{
|
||||
terrain::{Block, TerrainChunk},
|
||||
trade::{PendingTrade, TradeId, TradeResult},
|
||||
uid::Uid,
|
||||
uuid::Uuid,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -191,7 +192,7 @@ pub enum DisconnectReason {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum RegisterError {
|
||||
AlreadyLoggedIn,
|
||||
AlreadyLoggedIn(Uuid, String),
|
||||
AuthError(String),
|
||||
Banned(String),
|
||||
Kicked(String),
|
||||
|
@ -31,6 +31,15 @@ pub struct PendingLogin {
|
||||
pending_r: oneshot::Receiver<Result<(String, Uuid), RegisterError>>,
|
||||
}
|
||||
|
||||
impl PendingLogin {
|
||||
pub(crate) fn new_success(username: String, uuid: Uuid) -> Self {
|
||||
let (pending_s, pending_r) = oneshot::channel();
|
||||
let _ = pending_s.send(Ok((username, uuid)));
|
||||
|
||||
Self { pending_r }
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for PendingLogin {
|
||||
type Storage = IdvStorage<Self>;
|
||||
}
|
||||
@ -68,7 +77,7 @@ impl LoginProvider {
|
||||
fn login(&mut self, uuid: Uuid, username: String) -> Result<(), RegisterError> {
|
||||
// make sure that the user is not logged in already
|
||||
if self.accounts.contains_key(&uuid) {
|
||||
return Err(RegisterError::AlreadyLoggedIn);
|
||||
return Err(RegisterError::AlreadyLoggedIn(uuid, username));
|
||||
}
|
||||
info!(?username, "New User");
|
||||
self.accounts.insert(uuid, username);
|
||||
|
@ -6,12 +6,13 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
comp::{Admin, Player, Stats},
|
||||
event::{EventBus, ServerEvent},
|
||||
uid::{Uid, UidAllocator},
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
use common_net::msg::{
|
||||
CharacterInfo, ClientRegister, PlayerInfo, PlayerListUpdate, RegisterError, ServerGeneral,
|
||||
ServerRegisterAnswer,
|
||||
CharacterInfo, ClientRegister, DisconnectReason, PlayerInfo, PlayerListUpdate, RegisterError,
|
||||
ServerGeneral, ServerRegisterAnswer,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use plugin_api::Health;
|
||||
@ -48,6 +49,7 @@ impl<'a> System<'a> for Sys {
|
||||
WriteExpect<'a, LoginProvider>,
|
||||
WriteStorage<'a, Admin>,
|
||||
ReadExpect<'a, EditableSettings>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
);
|
||||
|
||||
const NAME: &'static str = "msg::register";
|
||||
@ -70,6 +72,7 @@ impl<'a> System<'a> for Sys {
|
||||
mut login_provider,
|
||||
mut admins,
|
||||
editable_settings,
|
||||
server_event_bus,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
// Player list to send new players.
|
||||
@ -100,6 +103,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
let mut finished_pending = vec![];
|
||||
let mut retries = vec![];
|
||||
for (entity, client, mut pending) in (&entities, &clients, &mut pending_logins).join() {
|
||||
if let Err(e) = || -> std::result::Result<(), crate::error::Error> {
|
||||
#[cfg(feature = "plugins")]
|
||||
@ -127,7 +131,38 @@ impl<'a> System<'a> for Sys {
|
||||
trace!(?r, "pending login returned");
|
||||
match r {
|
||||
Err(e) => {
|
||||
client.send(ServerRegisterAnswer::Err(e))?;
|
||||
let mut retry = false;
|
||||
if let RegisterError::AlreadyLoggedIn(uuid, ref username) = e {
|
||||
if let Some((old_entity, old_client, _)) =
|
||||
(&entities, &clients, &players)
|
||||
.join()
|
||||
.find(|(_, _, old_player)| old_player.uuid() == uuid)
|
||||
{
|
||||
// Remove old client
|
||||
server_event_bus
|
||||
.emit_now(ServerEvent::ClientDisconnect(old_entity));
|
||||
let _ = old_client.send(ServerGeneral::Disconnect(
|
||||
DisconnectReason::Kicked(String::from(
|
||||
"You have logged in from another location.",
|
||||
)),
|
||||
));
|
||||
// We can't login the new client right now as the
|
||||
// removal of the old client and player occurs later in
|
||||
// the tick, so we instead setup the new login to be
|
||||
// processed in the next tick
|
||||
// Create "fake" successful pending auth and mark it to
|
||||
// be inserted into pending_logins at the end of this
|
||||
// run
|
||||
retries.push((
|
||||
entity,
|
||||
PendingLogin::new_success(username.to_string(), uuid),
|
||||
));
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
if !retry {
|
||||
client.send(ServerRegisterAnswer::Err(e))?;
|
||||
}
|
||||
return Ok(());
|
||||
},
|
||||
Ok((username, uuid)) => (username, uuid),
|
||||
@ -174,6 +209,10 @@ impl<'a> System<'a> for Sys {
|
||||
for e in finished_pending {
|
||||
pending_logins.remove(e);
|
||||
}
|
||||
// Insert retry attempts back into pending_logins to be processed next tick
|
||||
for (entity, pending) in retries {
|
||||
let _ = pending_logins.insert(entity, pending);
|
||||
}
|
||||
|
||||
// Handle new players.
|
||||
// Tell all clients to add them to the player list.
|
||||
|
Loading…
Reference in New Issue
Block a user