diff --git a/src/common/interserver.rs b/src/common/interserver.rs index bd09913..cddaba7 100644 --- a/src/common/interserver.rs +++ b/src/common/interserver.rs @@ -8,7 +8,7 @@ pub struct ServerId(pub usize); #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct AuthToken(pub String); -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Ship { pub name: String, pub ip: Ipv4Addr, @@ -38,7 +38,8 @@ pub enum ShipMessage { message: String, }, RequestShipList, - + AddUser(UserAccountId), + RemoveUser(UserAccountId), } diff --git a/src/common/mainloop/interserver.rs b/src/common/mainloop/interserver.rs index e788993..7e4b95d 100644 --- a/src/common/mainloop/interserver.rs +++ b/src/common/mainloop/interserver.rs @@ -204,7 +204,6 @@ pub fn login_listen_mainloop(state: Arc Ok(RecvCharacterPacket::CharacterPreview(CharacterPreview::from_bytes(data)?)), 0xEC => Ok(RecvCharacterPacket::SetFlag(SetFlag::from_bytes(data)?)), 0x10 => Ok(RecvCharacterPacket::MenuSelect(MenuSelect::from_bytes(data)?)), + 0x09 => Ok(RecvCharacterPacket::MenuDetail(MenuDetail::from_bytes(data)?)), _ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec())) } } @@ -94,6 +97,7 @@ pub enum SendCharacterPacket { Timestamp(Timestamp), ShipList(ShipList), RedirectClient(RedirectClient), + SmallLeftDialog(SmallLeftDialog), } impl SendServerPacket for SendCharacterPacket { @@ -112,6 +116,7 @@ impl SendServerPacket for SendCharacterPacket { SendCharacterPacket::Timestamp(pkt) => pkt.as_bytes(), SendCharacterPacket::ShipList(pkt) => pkt.as_bytes(), SendCharacterPacket::RedirectClient(pkt) => pkt.as_bytes(), + SendCharacterPacket::SmallLeftDialog(pkt) => pkt.as_bytes(), } } } @@ -168,6 +173,11 @@ impl ClientState { } } +#[derive(Clone)] +struct ConnectedClient { + ship_id: Option, + expires: Option>, +} pub struct CharacterServerState { entity_gateway: EG, @@ -177,6 +187,8 @@ pub struct CharacterServerState { ships: BTreeMap, level_table: CharacterLevelTable, auth_token: AuthToken, + + connected_clients: BTreeMap, authenticated_ships: BTreeSet, ship_sender: BTreeMap>, } @@ -298,6 +310,7 @@ impl CharacterServerState { auth_token: auth_token, authenticated_ships: BTreeSet::new(), ship_sender: BTreeMap::new(), + connected_clients: BTreeMap::new(), } } @@ -306,23 +319,35 @@ impl CharacterServerState { } async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result, anyhow::Error> { - let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?; - Ok(match get_login_status(&self.entity_gateway, pkt).await.and_then(check_if_already_online) { - Ok(mut user) => { - user.at_character = true; - self.entity_gateway.save_user(&user).await?; + match get_login_status(&self.entity_gateway, pkt).await { + Ok(user) => { + if let Some(connected_client) = self.connected_clients.get(&user.id) { + if let Some(expires) = connected_client.expires { + if expires < chrono::Utc::now() { + return Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(AccountStatus::AlreadyOnline, Session::new()))]); + } + } + } let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new()); response.guildcard = user.guildcard; response.team_id = user.team_id.map_or(0, |ti| ti) as u32; + + let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?; + + self.connected_clients.insert(user.id.clone(), ConnectedClient { + ship_id: None, + expires: Some(chrono::Utc::now() + chrono::Duration::minutes(1)), + }); + client.user = Some(user); client.session = pkt.session; - vec![SendCharacterPacket::LoginResponse(response)] + Ok(vec![SendCharacterPacket::LoginResponse(response)]) }, Err(err) => { - vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(err, Session::new()))] + Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(err, Session::new()))]) } - }) + } } fn send_ship_list(&mut self, _id: ClientId, _pkt: &Login) -> Result, anyhow::Error> { @@ -490,9 +515,19 @@ impl CharacterServerState { } let ship = self.ships.get(&ServerId(menuselect.item as usize)) - .ok_or(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?; + .ok_or(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?; Ok(vec![SendCharacterPacket::RedirectClient(RedirectClient::new(u32::from_le_bytes(ship.ip.octets()), ship.port))]) } + + fn ship_detail(&mut self, menudetail: &MenuDetail) -> Result, anyhow::Error> { + let players = self.connected_clients.iter() + .filter(|(_, client)| { + client.ship_id == Some(ServerId(menudetail.item as usize)) + }) + .count(); + let ship_details = format!("players: {}\nrooms: {}", players, 0); + Ok(vec![SendCharacterPacket::SmallLeftDialog(SmallLeftDialog::new(ship_details))]) + } } #[async_trait::async_trait] @@ -557,6 +592,12 @@ impl ServerState for CharacterServerState { }, RecvCharacterPacket::MenuSelect(menuselect) => { Box::new(self.select_ship(menuselect)?.into_iter().map(move |pkt| (id, pkt))) + }, + RecvCharacterPacket::MenuDetail(menudetail) => { + match menudetail.menu { + SHIP_MENU_ID => Box::new(self.ship_detail(menudetail)?.into_iter().map(move |pkt| (id, pkt))), + _ => Box::new(Vec::new().into_iter()) + } } }) } @@ -588,20 +629,59 @@ impl InterserverActor for CharacterServerState { if self.auth_token == auth_token { self.authenticated_ships.insert(id); } + Ok(Vec::new()) }, ShipMessage::NewShip(new_ship) => { if self.authenticated_ships.contains(&id) { self.ships.insert(id, new_ship); } + Ok(Vec::new()) + }, + ShipMessage::AddUser(new_user) => { + if self.authenticated_ships.contains(&id) { + self.connected_clients.insert(new_user, ConnectedClient { + ship_id: Some(id), + expires: None, + }); + } + Ok(Vec::new()) + }, + ShipMessage::RemoveUser(new_user) => { + if self.authenticated_ships.contains(&id) { + self.connected_clients.remove(&new_user); + } + Ok(Vec::new()) + }, + ShipMessage::RequestShipList => { + if self.authenticated_ships.contains(&id) { + Ok(vec![(id, LoginMessage::ShipList { + ships: self.ships + .iter() + .map(|(_, ship)| { + ship + }) + .cloned() + .collect() + })]) + } + else { + Ok(Vec::new()) + } + }, + ShipMessage::SendMail{..} => { + Ok(Vec::new()) }, - _ => {} } - Ok(Vec::new()) } async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> { self.ships.remove(&id); self.ship_sender.remove(&id); + self.connected_clients = self.connected_clients.clone().into_iter() + .filter(|(_, client)| { + client.ship_id != Some(id) + }) + .collect(); Vec::new() } } diff --git a/src/ship/packet/handler/auth.rs b/src/ship/packet/handler/auth.rs index 16228c5..b691f25 100644 --- a/src/ship/packet/handler/auth.rs +++ b/src/ship/packet/handler/auth.rs @@ -5,19 +5,18 @@ use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients}; use crate::login::login::{get_login_status, check_if_already_online}; use crate::entity::gateway::EntityGateway; use crate::ship::items::ItemManager; +use crate::common::interserver::ShipMessage; pub async fn validate_login(id: ClientId, pkt: &Login, entity_gateway: &mut EG, clients: &mut Clients, item_manager: &mut ItemManager, + shipgate_sender: &Option>, ship_name: &String) -> Result, anyhow::Error> { - Ok(match get_login_status(entity_gateway, pkt).await.and_then(check_if_already_online) { + Ok(match get_login_status(entity_gateway, pkt).await { Ok(mut user) => { - user.at_ship= true; - entity_gateway.save_user(&user).await?; - let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new()); response.guildcard = user.id.0 as u32; response.team_id = user.team_id.map_or(31, |ti| ti) as u32; @@ -30,6 +29,10 @@ pub async fn validate_login(id: ClientId, let settings = entity_gateway.get_user_settings_by_user(&user).await?; item_manager.load_character(entity_gateway, &character).await?; + + if let Some(shipgate_sender) = shipgate_sender.as_ref() { + shipgate_sender(ShipMessage::AddUser(user.id.clone())); + } clients.insert(id, ClientState::new(user, settings, character, pkt.session)); vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(&&ship_name, 3))] }, diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 93a5487..6651999 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -351,6 +351,7 @@ impl ShipServerStateBuilder { port: self.port.unwrap_or(SHIP_PORT), shops: Box::new(ItemShops::new()), auth_token: self.auth_token.unwrap_or(AuthToken("".into())), + ship_list: Vec::new(), shipgate_sender: None, } } @@ -370,7 +371,8 @@ pub struct ShipServerState { shops: Box, auth_token: AuthToken, - shipgate_sender: Option>, + ship_list: Vec, + shipgate_sender: Option>, } impl ShipServerState { @@ -378,7 +380,7 @@ impl ShipServerState { ShipServerStateBuilder::new() } - pub fn set_sender(&mut self, sender: Box) { + pub fn set_sender(&mut self, sender: Box) { self.shipgate_sender = Some(sender); } @@ -496,7 +498,7 @@ impl ServerState for ShipServerState { -> Result + Send>, anyhow::Error> { Ok(match pkt { RecvShipPacket::Login(login) => { - Box::new(handler::auth::validate_login(id, login, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager, &self.name).await?.into_iter().map(move |pkt| (id, pkt))) + Box::new(handler::auth::validate_login(id, login, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager, &self.shipgate_sender, &self.name).await?.into_iter().map(move |pkt| (id, pkt))) }, RecvShipPacket::QuestDetailRequest(questdetailrequest) => { match questdetailrequest.menu { @@ -601,7 +603,6 @@ impl ServerState for ShipServerState { } async fn on_disconnect(&mut self, id: ClientId) -> Result, anyhow::Error> { - // TODO: don't unwrap! let client = self.clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let area_client = self.client_location.get_local_client(id)?; let neighbors = self.client_location.get_client_neighbors(id)?; @@ -611,15 +612,19 @@ impl ServerState for ShipServerState { if neighbors.len() == 0 { self.rooms[room.0] = None; } - let leader = self.client_location.get_room_leader(room).unwrap(); + let leader = self.client_location.get_room_leader(room)?; SendShipPacket::LeaveRoom(LeaveRoom::new(area_client.local_client.id(), leader.local_client.id())) }, RoomLobby::Lobby(lobby) => { - let leader = self.client_location.get_lobby_leader(lobby).unwrap(); + let leader = self.client_location.get_lobby_leader(lobby)?; SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())) } }; + if let Some(shipgate_sender) = self.shipgate_sender.as_ref() { + shipgate_sender(ShipMessage::RemoveUser(client.user.id.clone())); + } + self.client_location.remove_client_from_area(id); self.item_manager.remove_character_from_room(&client.character); @@ -652,8 +657,23 @@ impl InterserverActor for ShipServerState { })) ] } - async fn action(&mut self, _id: ServerId, _msg: Self::RecvMessage) -> Result, Self::Error> { - Ok(Vec::new()) + async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result, Self::Error> { + match msg { + LoginMessage::SendMail{..} => { + Ok(Vec::new()) + }, + LoginMessage::ShipList{ships} => { + self.ship_list = ships; + Ok(Vec::new()) + }, + LoginMessage::RequestUsers => { + Ok(self.clients.iter() + .map(|(_, client)| { + (id, ShipMessage::AddUser(client.user.id.clone())) + }) + .collect()) + } + } } async fn on_disconnect(&mut self, _id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {