Browse Source

Merge pull request 'interserver stuff and ship/block transfering' (#268) from one_day_i'll_have_useful_branch_names_again into master

pbs
jake 4 years ago
parent
commit
c22b23ef03
  1. 9
      src/common/interserver.rs
  2. 23
      src/common/mainloop/interserver.rs
  3. 3
      src/entity/account.rs
  4. 163
      src/login/character.rs
  5. 42
      src/ship/location.rs
  6. 2
      src/ship/packet/builder/lobby.rs
  7. 1
      src/ship/packet/builder/mod.rs
  8. 29
      src/ship/packet/builder/ship.rs
  9. 18
      src/ship/packet/handler/auth.rs
  10. 26
      src/ship/packet/handler/lobby.rs
  11. 1
      src/ship/packet/handler/mod.rs
  12. 23
      src/ship/packet/handler/ship.rs
  13. 223
      src/ship/ship.rs
  14. 8
      tests/test_exp_gain.rs

9
src/common/interserver.rs

@ -1,6 +1,7 @@
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use crate::entity::account::UserAccountId;
use crate::entity::character::CharacterEntityId; use crate::entity::character::CharacterEntityId;
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Copy, Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
@ -8,7 +9,7 @@ pub struct ServerId(pub usize);
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AuthToken(pub String); pub struct AuthToken(pub String);
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ship { pub struct Ship {
pub name: String, pub name: String,
pub ip: Ipv4Addr, pub ip: Ipv4Addr,
@ -25,7 +26,8 @@ pub enum LoginMessage {
}, },
ShipList { ShipList {
ships: Vec<Ship>, ships: Vec<Ship>,
}
},
RequestUsers,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -38,7 +40,8 @@ pub enum ShipMessage {
message: String, message: String,
}, },
RequestShipList, RequestShipList,
AddUser(UserAccountId),
RemoveUser(UserAccountId),
} }

23
src/common/mainloop/interserver.rs

@ -186,7 +186,7 @@ pub fn login_listen_mainloop<EG: EntityGateway + 'static>(state: Arc<Mutex<Chara
let mut id = 0; let mut id = 0;
let (server_state_sender, server_state_receiver) = async_std::sync::channel(1024); let (server_state_sender, server_state_receiver) = async_std::sync::channel(1024);
interserver_state_loop(state, server_state_receiver).await;
interserver_state_loop(state.clone(), server_state_receiver).await;
loop { loop {
let (socket, addr) = listener.accept().await.unwrap(); let (socket, addr) = listener.accept().await.unwrap();
@ -194,9 +194,16 @@ pub fn login_listen_mainloop<EG: EntityGateway + 'static>(state: Arc<Mutex<Chara
id += 1; id += 1;
let server_id = crate::common::interserver::ServerId(id); let server_id = crate::common::interserver::ServerId(id);
let (client_sender, client_receiver) = async_std::sync::channel(64); let (client_sender, client_receiver) = async_std::sync::channel(64);
{
let mut state = state.lock().await;
let local_sender = client_sender.clone();
state.set_sender(server_id, Box::new(move |message| {
async_std::task::block_on(local_sender.send(message));
}))
}
login_recv_loop(server_id, socket.clone(), server_state_sender.clone(), client_sender).await; login_recv_loop(server_id, socket.clone(), server_state_sender.clone(), client_sender).await;
interserver_send_loop(server_id, socket.clone(), client_receiver).await; interserver_send_loop(server_id, socket.clone(), client_receiver).await;
} }
@ -206,9 +213,9 @@ pub fn login_listen_mainloop<EG: EntityGateway + 'static>(state: Arc<Mutex<Chara
pub fn ship_connect_mainloop<EG: EntityGateway + 'static>(state: Arc<Mutex<ShipServerState<EG>>>, ip: std::net::Ipv4Addr, port: u16) -> Pin<Box<dyn Future<Output = ()>>> { pub fn ship_connect_mainloop<EG: EntityGateway + 'static>(state: Arc<Mutex<ShipServerState<EG>>>, ip: std::net::Ipv4Addr, port: u16) -> Pin<Box<dyn Future<Output = ()>>> {
Box::pin(async_std::task::spawn(async move { Box::pin(async_std::task::spawn(async move {
let mut id = 0; let mut id = 0;
let (server_state_sender, server_state_receiver) = async_std::sync::channel(1024); let (server_state_sender, server_state_receiver) = async_std::sync::channel(1024);
interserver_state_loop(state, server_state_receiver).await;
interserver_state_loop(state.clone(), server_state_receiver).await;
loop { loop {
info!("trying to connect to loginserv"); info!("trying to connect to loginserv");
@ -225,6 +232,14 @@ pub fn ship_connect_mainloop<EG: EntityGateway + 'static>(state: Arc<Mutex<ShipS
info!("found loginserv: {:?} {:?}", server_id, socket); info!("found loginserv: {:?} {:?}", server_id, socket);
let (client_sender, client_receiver) = async_std::sync::channel(64); let (client_sender, client_receiver) = async_std::sync::channel(64);
{
let mut state = state.lock().await;
let local_sender = client_sender.clone();
state.set_sender(Box::new(move |message| {
async_std::task::block_on(local_sender.send(message));
}))
}
login_recv_loop(server_id, socket.clone(), server_state_sender.clone(), client_sender).await; login_recv_loop(server_id, socket.clone(), server_state_sender.clone(), client_sender).await;
interserver_send_loop(server_id, socket.clone(), client_receiver).await; interserver_send_loop(server_id, socket.clone(), client_receiver).await;

3
src/entity/account.rs

@ -1,10 +1,11 @@
use serde::{Serialize, Deserialize};
use libpso::character::settings; use libpso::character::settings;
use libpso::character::guildcard; use libpso::character::guildcard;
pub const USERFLAG_NEWCHAR: u32 = 0x00000001; pub const USERFLAG_NEWCHAR: u32 = 0x00000001;
pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002; pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct UserAccountId(pub u32); pub struct UserAccountId(pub u32);
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UserSettingsId(pub u32); pub struct UserSettingsId(pub u32);

163
src/login/character.rs

@ -6,6 +6,7 @@ use rand::Rng;
use crc::{crc32, Hasher32}; use crc::{crc32, Hasher32};
use libpso::packet::login::*; use libpso::packet::login::*;
use libpso::packet::ship::{MenuDetail, SmallLeftDialog};
use libpso::{PacketParseError, PSOPacket}; use libpso::{PacketParseError, PSOPacket};
use libpso::crypto::bb::PSOBBCipher; use libpso::crypto::bb::PSOBBCipher;
use crate::entity::item; use crate::entity::item;
@ -17,8 +18,8 @@ use crate::common::interserver::{ServerId, InterserverActor, LoginMessage, ShipM
use crate::common::leveltable::CharacterLevelTable; use crate::common::leveltable::CharacterLevelTable;
use libpso::{utf8_to_array, utf8_to_utf16_array}; use libpso::{utf8_to_array, utf8_to_utf16_array};
use crate::entity::gateway::EntityGateway;
use crate::entity::account::{UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
use crate::entity::gateway::{EntityGateway, GatewayError};
use crate::entity::account::{UserAccountId, UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
use crate::entity::item::{NewItemEntity, ItemDetail, ItemLocation, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity}; use crate::entity::item::{NewItemEntity, ItemDetail, ItemLocation, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity};
use crate::entity::item::weapon::Weapon; use crate::entity::item::weapon::Weapon;
use crate::entity::item::armor::Armor; use crate::entity::item::armor::Armor;
@ -31,7 +32,7 @@ use crate::login::login::{get_login_status, check_if_already_online};
use crate::common::interserver::AuthToken; use crate::common::interserver::AuthToken;
pub const CHARACTER_PORT: u16 = 12001; pub const CHARACTER_PORT: u16 = 12001;
const SHIP_MENU_ID: u32 = 1;
pub const SHIP_MENU_ID: u32 = 1;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
#[error("")] #[error("")]
@ -41,7 +42,7 @@ pub enum CharacterError {
CouldNotLoadSettings, CouldNotLoadSettings,
CouldNotLoadCharacters, CouldNotLoadCharacters,
CouldNotLoadGuildcard, CouldNotLoadGuildcard,
DbError,
GatewayError(#[from] GatewayError),
} }
#[derive(Debug)] #[derive(Debug)]
@ -57,6 +58,7 @@ pub enum RecvCharacterPacket {
CharacterPreview(CharacterPreview), CharacterPreview(CharacterPreview),
SetFlag(SetFlag), SetFlag(SetFlag),
MenuSelect(MenuSelect), MenuSelect(MenuSelect),
MenuDetail(MenuDetail),
} }
impl RecvServerPacket for RecvCharacterPacket { impl RecvServerPacket for RecvCharacterPacket {
@ -73,6 +75,7 @@ impl RecvServerPacket for RecvCharacterPacket {
0xE5 => Ok(RecvCharacterPacket::CharacterPreview(CharacterPreview::from_bytes(data)?)), 0xE5 => Ok(RecvCharacterPacket::CharacterPreview(CharacterPreview::from_bytes(data)?)),
0xEC => Ok(RecvCharacterPacket::SetFlag(SetFlag::from_bytes(data)?)), 0xEC => Ok(RecvCharacterPacket::SetFlag(SetFlag::from_bytes(data)?)),
0x10 => Ok(RecvCharacterPacket::MenuSelect(MenuSelect::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())) _ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec()))
} }
} }
@ -94,6 +97,7 @@ pub enum SendCharacterPacket {
Timestamp(Timestamp), Timestamp(Timestamp),
ShipList(ShipList), ShipList(ShipList),
RedirectClient(RedirectClient), RedirectClient(RedirectClient),
SmallLeftDialog(SmallLeftDialog),
} }
impl SendServerPacket for SendCharacterPacket { impl SendServerPacket for SendCharacterPacket {
@ -112,6 +116,7 @@ impl SendServerPacket for SendCharacterPacket {
SendCharacterPacket::Timestamp(pkt) => pkt.as_bytes(), SendCharacterPacket::Timestamp(pkt) => pkt.as_bytes(),
SendCharacterPacket::ShipList(pkt) => pkt.as_bytes(), SendCharacterPacket::ShipList(pkt) => pkt.as_bytes(),
SendCharacterPacket::RedirectClient(pkt) => pkt.as_bytes(), SendCharacterPacket::RedirectClient(pkt) => pkt.as_bytes(),
SendCharacterPacket::SmallLeftDialog(pkt) => pkt.as_bytes(),
} }
} }
} }
@ -168,6 +173,11 @@ impl ClientState {
} }
} }
#[derive(Debug, Clone)]
struct ConnectedClient {
ship_id: Option<ServerId>,
expires: Option<chrono::DateTime<chrono::Utc>>,
}
pub struct CharacterServerState<EG: EntityGateway> { pub struct CharacterServerState<EG: EntityGateway> {
entity_gateway: EG, entity_gateway: EG,
@ -177,11 +187,14 @@ pub struct CharacterServerState<EG: EntityGateway> {
ships: BTreeMap<ServerId, Ship>, ships: BTreeMap<ServerId, Ship>,
level_table: CharacterLevelTable, level_table: CharacterLevelTable,
auth_token: AuthToken, auth_token: AuthToken,
connected_clients: BTreeMap<UserAccountId, ConnectedClient>,
authenticated_ships: BTreeSet<ServerId>, authenticated_ships: BTreeSet<ServerId>,
ship_sender: BTreeMap<ServerId, Box<dyn Fn(LoginMessage) + Send>>,
} }
async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccountEntity, preview: &CharacterPreview) {
async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccountEntity, preview: &CharacterPreview) -> Result<(), anyhow::Error> {
let mut character = new_character_from_preview(user, preview); let mut character = new_character_from_preview(user, preview);
match character.char_class { match character.char_class {
CharacterClass::FOmar | CharacterClass::FOmarl| CharacterClass::FOnewm | CharacterClass::FOnewearl => character.techs.set_tech(Technique::Foie, TechLevel(1)), CharacterClass::FOmar | CharacterClass::FOmarl| CharacterClass::FOnewm | CharacterClass::FOnewearl => character.techs.set_tech(Technique::Foie, TechLevel(1)),
@ -189,7 +202,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
} }
character.meseta = 300; character.meseta = 300;
let character = entity_gateway.create_character(character).await.unwrap();
let character = entity_gateway.create_character(character).await?;
let new_weapon = match character.char_class { let new_weapon = match character.char_class {
CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber, CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber,
@ -211,7 +224,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
}}).await.unwrap();
}}).await?;
let armor = entity_gateway.create_item( let armor = entity_gateway.create_item(
NewItemEntity { NewItemEntity {
@ -225,7 +238,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
}}).await.unwrap();
}}).await?;
let mut mag = { let mut mag = {
if character.char_class.is_android() { if character.char_class.is_android() {
@ -240,7 +253,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
item: ItemDetail::Mag(mag), item: ItemDetail::Mag(mag),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
}}).await.unwrap();
}}).await?;
let mut monomates = Vec::new(); let mut monomates = Vec::new();
for _ in 0..4usize { for _ in 0..4usize {
@ -252,7 +265,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
}}).await.unwrap())
}}).await?)
} }
let mut monofluids = Vec::new(); let mut monofluids = Vec::new();
@ -265,20 +278,21 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
}}).await.unwrap())
}}).await?)
} }
let inventory = InventoryEntity { let inventory = InventoryEntity {
items: vec![InventoryItemEntity::Individual(weapon.clone()), InventoryItemEntity::Individual(armor.clone()), InventoryItemEntity::Individual(mag.clone()), items: vec![InventoryItemEntity::Individual(weapon.clone()), InventoryItemEntity::Individual(armor.clone()), InventoryItemEntity::Individual(mag.clone()),
InventoryItemEntity::Stacked(monomates), InventoryItemEntity::Stacked(monofluids)], InventoryItemEntity::Stacked(monomates), InventoryItemEntity::Stacked(monofluids)],
}; };
entity_gateway.set_character_inventory(&character.id, &inventory).await;
entity_gateway.set_character_bank(&character.id, &BankEntity::default(), BankName("".into())).await;
entity_gateway.set_character_inventory(&character.id, &inventory).await?;
entity_gateway.set_character_bank(&character.id, &BankEntity::default(), BankName("".into())).await?;
let mut equipped = EquippedEntity::default(); let mut equipped = EquippedEntity::default();
equipped.weapon = Some(weapon.id); equipped.weapon = Some(weapon.id);
equipped.armor = Some(armor.id); equipped.armor = Some(armor.id);
equipped.mag = Some(mag.id); equipped.mag = Some(mag.id);
entity_gateway.set_character_equips(&character.id, &equipped).await;
entity_gateway.set_character_equips(&character.id, &equipped).await?;
Ok(())
} }
@ -295,27 +309,48 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
level_table: CharacterLevelTable::new(), level_table: CharacterLevelTable::new(),
auth_token: auth_token, auth_token: auth_token,
authenticated_ships: BTreeSet::new(), authenticated_ships: BTreeSet::new(),
ship_sender: BTreeMap::new(),
connected_clients: BTreeMap::new(),
}
} }
pub fn set_sender(&mut self, server_id: ServerId, sender: Box<dyn Fn(LoginMessage) + Send>) {
self.ship_sender.insert(server_id, sender);
} }
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendCharacterPacket>, anyhow::Error> { async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendCharacterPacket>, 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.map_err(|_| CharacterError::DbError)?;
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()))]);
}
}
else {
return Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(AccountStatus::AlreadyOnline, Session::new()))]);
}
}
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new()); let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
response.guildcard = user.guildcard; response.guildcard = user.guildcard;
response.team_id = user.team_id.map_or(0, |ti| ti) as u32; 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: None, //Some(chrono::Utc::now() + chrono::Duration::minutes(1)),
});
client.user = Some(user); client.user = Some(user);
client.session = pkt.session; client.session = pkt.session;
vec![SendCharacterPacket::LoginResponse(response)]
Ok(vec![SendCharacterPacket::LoginResponse(response)])
}, },
Err(err) => { 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<Vec<SendCharacterPacket>, anyhow::Error> { fn send_ship_list(&mut self, _id: ClientId, _pkt: &Login) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
@ -457,7 +492,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?; let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
let mut user = client.user.as_mut().unwrap(); let mut user = client.user.as_mut().unwrap();
if user.flags == USERFLAG_NEWCHAR { if user.flags == USERFLAG_NEWCHAR {
new_character(&mut self.entity_gateway, &user, preview).await
new_character(&mut self.entity_gateway, &user, preview).await?
} }
if user.flags == USERFLAG_DRESSINGROOM { if user.flags == USERFLAG_DRESSINGROOM {
// TODO: dressing room stuff // TODO: dressing room stuff
@ -477,15 +512,33 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
]) ])
} }
fn select_ship(&mut self, menuselect: &MenuSelect) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
fn select_ship(&mut self, id: ClientId, menuselect: &MenuSelect) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
if menuselect.menu != SHIP_MENU_ID { if menuselect.menu != SHIP_MENU_ID {
Err(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?; Err(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?;
} }
if let Some(client) = self.clients.get(&id) {
if let Some(user) = &client.user {
if let Some(cc) = self.connected_clients.get_mut(&user.id) {
cc.ship_id = Some(ServerId(menuselect.item as usize));
}
}
}
let ship = self.ships.get(&ServerId(menuselect.item as usize)) 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))]) Ok(vec![SendCharacterPacket::RedirectClient(RedirectClient::new(u32::from_le_bytes(ship.ip.octets()), ship.port))])
} }
fn ship_detail(&mut self, menudetail: &MenuDetail) -> Result<Vec<SendCharacterPacket>, 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] #[async_trait::async_trait]
@ -549,16 +602,21 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
Box::new(self.character_preview(id, preview).await?.into_iter().map(move |pkt| (id, pkt))) Box::new(self.character_preview(id, preview).await?.into_iter().map(move |pkt| (id, pkt)))
}, },
RecvCharacterPacket::MenuSelect(menuselect) => { RecvCharacterPacket::MenuSelect(menuselect) => {
Box::new(self.select_ship(menuselect)?.into_iter().map(move |pkt| (id, pkt)))
Box::new(self.select_ship(id, 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())
}
} }
}) })
} }
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendCharacterPacket)>, anyhow::Error> { async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendCharacterPacket)>, anyhow::Error> {
if let Some(client) = self.clients.remove(&id) { if let Some(client) = self.clients.remove(&id) {
if let Some(mut user) = client.user {
user.at_character= false;
self.entity_gateway.save_user(&user).await;
if let Some(user) = client.user {
self.connected_clients.remove(&user.id);
} }
} }
Ok(Vec::new()) Ok(Vec::new())
@ -581,19 +639,66 @@ impl<EG: EntityGateway> InterserverActor for CharacterServerState<EG> {
if self.auth_token == auth_token { if self.auth_token == auth_token {
self.authenticated_ships.insert(id); self.authenticated_ships.insert(id);
} }
Ok(Vec::new())
}, },
ShipMessage::NewShip(new_ship) => { ShipMessage::NewShip(new_ship) => {
if self.authenticated_ships.contains(&id) { if self.authenticated_ships.contains(&id) {
self.ships.insert(id, new_ship); self.ships.insert(id, new_ship);
} }
let ships = self.ships.iter().map(|(_, s)| s).cloned().collect::<Vec<_>>();
Ok(self.ships
.iter()
.map(|(id, _)| {
(*id, LoginMessage::ShipList{ ships: ships.clone() })
})
.collect())
}, },
_ => {}
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()) 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())
},
}
} }
async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> { async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
self.ships.remove(&id); 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() Vec::new()
} }
} }

42
src/ship/location.rs

@ -133,16 +133,25 @@ pub struct ClientLocation {
client_location: HashMap<ClientId, RoomLobby>, client_location: HashMap<ClientId, RoomLobby>,
} }
impl ClientLocation {
pub fn new() -> ClientLocation {
impl Default for ClientLocation {
fn default() -> ClientLocation {
ClientLocation { ClientLocation {
lobbies: [Lobby([None; 12]); 15], lobbies: [Lobby([None; 12]); 15],
rooms: [None; MAX_ROOMS], rooms: [None; MAX_ROOMS],
client_location: HashMap::new(), client_location: HashMap::new(),
} }
} }
}
impl ClientLocation {
pub fn add_client_to_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result<(), JoinLobbyError> { pub fn add_client_to_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result<(), JoinLobbyError> {
let l = self.lobbies.get_mut(lobby.0).ok_or(JoinLobbyError::LobbyDoesNotExist)?;
if l.0.iter().filter(|c| c.is_none()).count() == 0 {
return Err(JoinLobbyError::LobbyFull);
}
self.remove_client_from_area(id);
let l = self.lobbies.get_mut(lobby.0).ok_or(JoinLobbyError::LobbyDoesNotExist)?; let l = self.lobbies.get_mut(lobby.0).ok_or(JoinLobbyError::LobbyDoesNotExist)?;
let (index, empty_slot) = l.0.iter_mut() let (index, empty_slot) = l.0.iter_mut()
.enumerate() .enumerate()
@ -154,7 +163,6 @@ impl ClientLocation {
local_client: LocalClientId(index), local_client: LocalClientId(index),
time_join: SystemTime::now(), time_join: SystemTime::now(),
}); });
self.remove_client_from_area(id);
self.client_location.insert(id, RoomLobby::Lobby(lobby)); self.client_location.insert(id, RoomLobby::Lobby(lobby));
Ok(()) Ok(())
} }
@ -389,7 +397,7 @@ mod test {
#[test] #[test]
fn test_add_client_to_lobby() { fn test_add_client_to_lobby() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
cl.add_client_to_lobby(ClientId(12), LobbyId(0)); cl.add_client_to_lobby(ClientId(12), LobbyId(0));
cl.add_client_to_lobby(ClientId(13), LobbyId(1)); cl.add_client_to_lobby(ClientId(13), LobbyId(1));
cl.add_client_to_lobby(ClientId(14), LobbyId(0)); cl.add_client_to_lobby(ClientId(14), LobbyId(0));
@ -402,7 +410,7 @@ mod test {
#[test] #[test]
fn test_add_client_to_full_lobby() { fn test_add_client_to_full_lobby() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
(0..12).for_each(|i| { (0..12).for_each(|i| {
cl.add_client_to_lobby(ClientId(i), LobbyId(0)); cl.add_client_to_lobby(ClientId(i), LobbyId(0));
}); });
@ -411,7 +419,7 @@ mod test {
#[test] #[test]
fn test_add_client_to_next_available_lobby() { fn test_add_client_to_next_available_lobby() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
(1..4).for_each(|lobby| { (1..4).for_each(|lobby| {
(0..12).for_each(|i| { (0..12).for_each(|i| {
cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)); cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby));
@ -422,7 +430,7 @@ mod test {
#[test] #[test]
fn test_add_to_lobby_when_all_are_full() { fn test_add_to_lobby_when_all_are_full() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
(0..15).for_each(|lobby| { (0..15).for_each(|lobby| {
(0..12).for_each(|i| { (0..12).for_each(|i| {
cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)); cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby));
@ -433,13 +441,13 @@ mod test {
#[test] #[test]
fn test_new_room() { fn test_new_room() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
assert!(cl.create_new_room(ClientId(12)) == Ok(RoomId(0))); assert!(cl.create_new_room(ClientId(12)) == Ok(RoomId(0)));
} }
#[test] #[test]
fn test_add_client_to_room() { fn test_add_client_to_room() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
let room = cl.create_new_room(ClientId(12)).unwrap(); let room = cl.create_new_room(ClientId(12)).unwrap();
assert!(cl.add_client_to_room(ClientId(234), room) == Ok(())); assert!(cl.add_client_to_room(ClientId(234), room) == Ok(()));
assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![ assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
@ -450,7 +458,7 @@ mod test {
#[test] #[test]
fn test_no_new_room_slots() { fn test_no_new_room_slots() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
for i in 0..128 { for i in 0..128 {
cl.create_new_room(ClientId(i)); cl.create_new_room(ClientId(i));
} }
@ -459,7 +467,7 @@ mod test {
#[test] #[test]
fn test_joining_full_room() { fn test_joining_full_room() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
let room = cl.create_new_room(ClientId(0)).unwrap(); let room = cl.create_new_room(ClientId(0)).unwrap();
assert!(cl.add_client_to_room(ClientId(1), room) == Ok(())); assert!(cl.add_client_to_room(ClientId(1), room) == Ok(()));
assert!(cl.add_client_to_room(ClientId(2), room) == Ok(())); assert!(cl.add_client_to_room(ClientId(2), room) == Ok(()));
@ -469,7 +477,7 @@ mod test {
#[test] #[test]
fn test_adding_client_to_room_removes_from_lobby() { fn test_adding_client_to_room_removes_from_lobby() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
cl.add_client_to_lobby(ClientId(93), LobbyId(0)); cl.add_client_to_lobby(ClientId(93), LobbyId(0));
cl.add_client_to_lobby(ClientId(23), LobbyId(0)); cl.add_client_to_lobby(ClientId(23), LobbyId(0));
cl.add_client_to_lobby(ClientId(51), LobbyId(0)); cl.add_client_to_lobby(ClientId(51), LobbyId(0));
@ -489,7 +497,7 @@ mod test {
#[test] #[test]
fn test_getting_neighbors() { fn test_getting_neighbors() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
cl.add_client_to_lobby(ClientId(93), LobbyId(0)); cl.add_client_to_lobby(ClientId(93), LobbyId(0));
cl.add_client_to_lobby(ClientId(23), LobbyId(0)); cl.add_client_to_lobby(ClientId(23), LobbyId(0));
cl.add_client_to_lobby(ClientId(51), LobbyId(0)); cl.add_client_to_lobby(ClientId(51), LobbyId(0));
@ -504,7 +512,7 @@ mod test {
#[test] #[test]
fn test_failing_to_join_lobby_does_not_remove_from_current_area() { fn test_failing_to_join_lobby_does_not_remove_from_current_area() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
(0..12).for_each(|i| { (0..12).for_each(|i| {
cl.add_client_to_lobby(ClientId(i), LobbyId(0)); cl.add_client_to_lobby(ClientId(i), LobbyId(0));
}); });
@ -518,7 +526,7 @@ mod test {
#[test] #[test]
fn test_get_leader() { fn test_get_leader() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
cl.add_client_to_lobby(ClientId(93), LobbyId(0)); cl.add_client_to_lobby(ClientId(93), LobbyId(0));
cl.add_client_to_lobby(ClientId(23), LobbyId(0)); cl.add_client_to_lobby(ClientId(23), LobbyId(0));
cl.add_client_to_lobby(ClientId(51), LobbyId(0)); cl.add_client_to_lobby(ClientId(51), LobbyId(0));
@ -529,7 +537,7 @@ mod test {
#[test] #[test]
fn test_remove_client_from_room() { fn test_remove_client_from_room() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
let room = cl.create_new_room(ClientId(51)).unwrap(); let room = cl.create_new_room(ClientId(51)).unwrap();
cl.add_client_to_room(ClientId(93), room); cl.add_client_to_room(ClientId(93), room);
cl.add_client_to_room(ClientId(23), room); cl.add_client_to_room(ClientId(23), room);
@ -545,7 +553,7 @@ mod test {
#[test] #[test]
fn test_leader_changes_on_leader_leaving() { fn test_leader_changes_on_leader_leaving() {
let mut cl = ClientLocation::new();
let mut cl = ClientLocation::default();
let room = cl.create_new_room(ClientId(51)).unwrap(); let room = cl.create_new_room(ClientId(51)).unwrap();
cl.add_client_to_room(ClientId(93), room); cl.add_client_to_room(ClientId(93), room);
cl.add_client_to_room(ClientId(23), room); cl.add_client_to_room(ClientId(23), room);

2
src/ship/packet/builder/lobby.rs

@ -13,7 +13,7 @@ pub fn join_lobby(id: ClientId,
clients: &Clients, clients: &Clients,
item_manager: &ItemManager, item_manager: &ItemManager,
level_table: &CharacterLevelTable) level_table: &CharacterLevelTable)
-> Result<JoinLobby, ShipError> {
-> Result<JoinLobby, anyhow::Error> {
let lobby_clients = client_location.get_clients_in_lobby(lobby).map_err(|err| -> ClientLocationError { err.into() })?; let lobby_clients = client_location.get_clients_in_lobby(lobby).map_err(|err| -> ClientLocationError { err.into() })?;
let playerinfo = lobby_clients.iter() let playerinfo = lobby_clients.iter()
.map(|area_client| { .map(|area_client| {

1
src/ship/packet/builder/mod.rs

@ -2,6 +2,7 @@ pub mod lobby;
pub mod message; pub mod message;
pub mod room; pub mod room;
pub mod quest; pub mod quest;
pub mod ship;
use libpso::character::character::Inventory; use libpso::character::character::Inventory;
use libpso::packet::ship::{PlayerHeader, PlayerInfo}; use libpso::packet::ship::{PlayerHeader, PlayerInfo};

29
src/ship/packet/builder/ship.rs

@ -0,0 +1,29 @@
use libpso::packet::login::{ShipList, ShipListEntry};
use libpso::packet::ship::*;
use crate::common::serverstate::ClientId;
use crate::common::leveltable::CharacterLevelTable;
use crate::ship::ship::{ShipError, ClientState, Clients};
use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationError};
use crate::ship::room::RoomState;
use crate::ship::items::ItemManager;
use crate::ship::packet::builder::{player_header, player_info};
use libpso::utf8_to_utf16_array;
use crate::common::interserver::Ship;
use libpso::packet::ship::BLOCK_MENU_ID;
use crate::login::character::SHIP_MENU_ID;
pub fn ship_list(ships: &Vec<Ship>) -> ShipList {
let ships = ships.iter()
.enumerate()
.map(|(i, ship)| {
ShipListEntry {
menu: SHIP_MENU_ID,
item: i as u32,
flags: 0,
name: utf8_to_utf16_array!(ship.name, 0x11)
}
})
.collect();
ShipList::new(ships)
}

18
src/ship/packet/handler/auth.rs

@ -5,19 +5,19 @@ use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients};
use crate::login::login::{get_login_status, check_if_already_online}; use crate::login::login::{get_login_status, check_if_already_online};
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use crate::ship::items::ItemManager; use crate::ship::items::ItemManager;
use crate::common::interserver::ShipMessage;
pub async fn validate_login<EG: EntityGateway>(id: ClientId, pub async fn validate_login<EG: EntityGateway>(id: ClientId,
pkt: &Login, pkt: &Login,
entity_gateway: &mut EG, entity_gateway: &mut EG,
clients: &mut Clients, clients: &mut Clients,
item_manager: &mut ItemManager, item_manager: &mut ItemManager,
ship_name: &String)
shipgate_sender: &Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
ship_name: &String,
num_blocks: usize)
-> Result<Vec<SendShipPacket>, anyhow::Error> { -> Result<Vec<SendShipPacket>, anyhow::Error> {
Ok(match get_login_status(entity_gateway, pkt).await.and_then(check_if_already_online) {
Ok(mut user) => {
user.at_ship= true;
entity_gateway.save_user(&user).await?;
Ok(match get_login_status(entity_gateway, pkt).await {
Ok(user) => {
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new()); let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
response.guildcard = user.id.0 as u32; response.guildcard = user.id.0 as u32;
response.team_id = user.team_id.map_or(31, |ti| ti) as u32; response.team_id = user.team_id.map_or(31, |ti| ti) as u32;
@ -30,8 +30,12 @@ pub async fn validate_login<EG: EntityGateway>(id: ClientId,
let settings = entity_gateway.get_user_settings_by_user(&user).await?; let settings = entity_gateway.get_user_settings_by_user(&user).await?;
item_manager.load_character(entity_gateway, &character).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)); clients.insert(id, ClientState::new(user, settings, character, pkt.session));
vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(&&ship_name, 3))]
vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(&&ship_name, num_blocks))]
}, },
Err(err) => { Err(err) => {
vec![SendShipPacket::LoginResponse(LoginResponse::by_status(err, Session::new()))] vec![SendShipPacket::LoginResponse(LoginResponse::by_status(err, Session::new()))]

26
src/ship/packet/handler/lobby.rs

@ -15,9 +15,9 @@ pub fn block_selected(id: ClientId,
clients: &mut Clients, clients: &mut Clients,
item_manager: &ItemManager, item_manager: &ItemManager,
level_table: &CharacterLevelTable) level_table: &CharacterLevelTable)
-> Result<Vec<SendShipPacket>, anyhow::Error> {
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
client.block = pkt.item as u32;
client.block = pkt.item as usize - 1;
let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp);
@ -38,11 +38,11 @@ pub fn block_selected(id: ClientId,
.build(); .build();
Ok(vec![ Ok(vec![
SendShipPacket::FullCharacter(Box::new(FullCharacter {
(id, SendShipPacket::FullCharacter(Box::new(FullCharacter {
character: fc, character: fc,
})),
SendShipPacket::CharDataRequest(CharDataRequest {}),
SendShipPacket::LobbyList(LobbyList::new()),
}))),
(id, SendShipPacket::CharDataRequest(CharDataRequest {})),
(id, SendShipPacket::LobbyList(LobbyList::new())),
]) ])
} }
@ -113,3 +113,17 @@ pub async fn change_lobby<EG: EntityGateway>(id: ClientId,
.map(|c| (c.client, SendShipPacket::LeaveLobby(leave_lobby.clone())))) .map(|c| (c.client, SendShipPacket::LeaveLobby(leave_lobby.clone()))))
.collect()) .collect())
} }
pub fn remove_from_lobby(id: ClientId,
client_location: &mut ClientLocation)
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
let area_client = client_location.get_local_client(id)?;
let neighbors = client_location.get_client_neighbors(id)?;
let leader = client_location.get_leader_by_client(id)?;
let leave_lobby_pkt = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()));
client_location.remove_client_from_area(id)?;
Ok(neighbors.into_iter().map(|n| {
(n.client, leave_lobby_pkt.clone())
}).collect())
}

1
src/ship/packet/handler/mod.rs

@ -6,3 +6,4 @@ pub mod message;
pub mod room; pub mod room;
pub mod settings; pub mod settings;
pub mod quest; pub mod quest;
pub mod ship;

23
src/ship/packet/handler/ship.rs

@ -0,0 +1,23 @@
use libpso::packet::ship::*;
use libpso::packet::login::RedirectClient;
use crate::common::serverstate::ClientId;
use crate::common::interserver::Ship;
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
use crate::ship::packet::builder;
pub fn ship_list(id: ClientId, ship_list: &Vec<Ship>)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
Box::new(vec![(id, SendShipPacket::ShipList(builder::ship::ship_list(ship_list)))].into_iter())
}
pub fn block_list(id: ClientId, shipname: &str, num_blocks: usize)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
Box::new(vec![(id, SendShipPacket::ShipBlockList(ShipBlockList::new(shipname, num_blocks)))].into_iter())
}
pub fn selected_ship(id: ClientId, menuselect: &MenuSelect, ship_list: &Vec<Ship>)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
let ship = ship_list.get(menuselect.item as usize).ok_or(ShipError::InvalidShip(menuselect.item as usize))?;
let ip = u32::from_ne_bytes(ship.ip.octets());
Ok(Box::new(vec![(id, SendShipPacket::RedirectClient(RedirectClient::new(ip, ship.port)))].into_iter()))
}

223
src/ship/ship.rs

@ -6,7 +6,7 @@ use rand::Rng;
use thiserror::Error; use thiserror::Error;
use libpso::packet::ship::*; use libpso::packet::ship::*;
use libpso::packet::login::{Login, LoginResponse, Session};
use libpso::packet::login::{RedirectClient, Login, LoginResponse, Session, ShipList};
use libpso::packet::messages::*; use libpso::packet::messages::*;
use libpso::{PacketParseError, PSOPacket}; use libpso::{PacketParseError, PSOPacket};
use libpso::crypto::bb::PSOBBCipher; use libpso::crypto::bb::PSOBBCipher;
@ -18,6 +18,8 @@ use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState
use crate::common::leveltable::CharacterLevelTable; use crate::common::leveltable::CharacterLevelTable;
use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage}; use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage};
use crate::login::character::SHIP_MENU_ID;
use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::gateway::{EntityGateway, GatewayError};
use crate::entity::account::{UserAccountEntity, UserSettingsEntity}; use crate::entity::account::{UserAccountEntity, UserSettingsEntity};
use crate::entity::character::{CharacterEntity, SectionID}; use crate::entity::character::{CharacterEntity, SectionID};
@ -67,6 +69,8 @@ pub enum ShipError {
ShopError, ShopError,
GatewayError(#[from] GatewayError), GatewayError(#[from] GatewayError),
UnknownMonster(crate::ship::monster::MonsterType), UnknownMonster(crate::ship::monster::MonsterType),
InvalidShip(usize),
InvalidBlock(usize),
} }
#[derive(Debug)] #[derive(Debug)]
@ -98,6 +102,8 @@ pub enum RecvShipPacket {
DoneLoadingQuest(DoneLoadingQuest), DoneLoadingQuest(DoneLoadingQuest),
FullCharacterData(Box<FullCharacterData>), FullCharacterData(Box<FullCharacterData>),
SaveOptions(SaveOptions), SaveOptions(SaveOptions),
RequestShipList(RequestShipList),
RequestShipBlockList(RequestShipBlockList),
} }
impl RecvServerPacket for RecvShipPacket { impl RecvServerPacket for RecvShipPacket {
@ -131,6 +137,8 @@ impl RecvServerPacket for RecvShipPacket {
0x6F => Ok(RecvShipPacket::DoneBursting(DoneBursting::from_bytes(data)?)), 0x6F => Ok(RecvShipPacket::DoneBursting(DoneBursting::from_bytes(data)?)),
0x16F => Ok(RecvShipPacket::DoneBursting2(DoneBursting2::from_bytes(data)?)), 0x16F => Ok(RecvShipPacket::DoneBursting2(DoneBursting2::from_bytes(data)?)),
0x84 => Ok(RecvShipPacket::LobbySelect(LobbySelect::from_bytes(data)?)), 0x84 => Ok(RecvShipPacket::LobbySelect(LobbySelect::from_bytes(data)?)),
0xA0 => Ok(RecvShipPacket::RequestShipList(RequestShipList::from_bytes(data)?)),
0xA1 => Ok(RecvShipPacket::RequestShipBlockList(RequestShipBlockList::from_bytes(data)?)),
0xA2 => Ok(RecvShipPacket::RequestQuestList(RequestQuestList::from_bytes(data)?)), 0xA2 => Ok(RecvShipPacket::RequestQuestList(RequestQuestList::from_bytes(data)?)),
0xAC => Ok(RecvShipPacket::DoneLoadingQuest(DoneLoadingQuest::from_bytes(data)?)), 0xAC => Ok(RecvShipPacket::DoneLoadingQuest(DoneLoadingQuest::from_bytes(data)?)),
0xE7 => Ok(RecvShipPacket::FullCharacterData(Box::new(FullCharacterData::from_bytes(data)?))), 0xE7 => Ok(RecvShipPacket::FullCharacterData(Box::new(FullCharacterData::from_bytes(data)?))),
@ -144,6 +152,7 @@ impl RecvServerPacket for RecvShipPacket {
pub enum SendShipPacket { pub enum SendShipPacket {
ShipWelcome(ShipWelcome), ShipWelcome(ShipWelcome),
LoginResponse(LoginResponse), LoginResponse(LoginResponse),
ShipList(ShipList),
ShipBlockList(ShipBlockList), ShipBlockList(ShipBlockList),
FullCharacter(Box<FullCharacter>), FullCharacter(Box<FullCharacter>),
CharDataRequest(CharDataRequest), CharDataRequest(CharDataRequest),
@ -172,6 +181,7 @@ pub enum SendShipPacket {
QuestChunk(QuestChunk), QuestChunk(QuestChunk),
DoneLoadingQuest(DoneLoadingQuest), DoneLoadingQuest(DoneLoadingQuest),
BankItemList(BankItemList), BankItemList(BankItemList),
RedirectClient(RedirectClient),
} }
impl SendServerPacket for SendShipPacket { impl SendServerPacket for SendShipPacket {
@ -179,6 +189,7 @@ impl SendServerPacket for SendShipPacket {
match self { match self {
SendShipPacket::ShipWelcome(pkt) => pkt.as_bytes(), SendShipPacket::ShipWelcome(pkt) => pkt.as_bytes(),
SendShipPacket::LoginResponse(pkt) => pkt.as_bytes(), SendShipPacket::LoginResponse(pkt) => pkt.as_bytes(),
SendShipPacket::ShipList(pkt) => pkt.as_bytes(),
SendShipPacket::ShipBlockList(pkt) => pkt.as_bytes(), SendShipPacket::ShipBlockList(pkt) => pkt.as_bytes(),
SendShipPacket::FullCharacter(pkt) => pkt.as_bytes(), SendShipPacket::FullCharacter(pkt) => pkt.as_bytes(),
SendShipPacket::CharDataRequest(pkt) => pkt.as_bytes(), SendShipPacket::CharDataRequest(pkt) => pkt.as_bytes(),
@ -207,6 +218,7 @@ impl SendServerPacket for SendShipPacket {
SendShipPacket::QuestChunk(pkt) => pkt.as_bytes(), SendShipPacket::QuestChunk(pkt) => pkt.as_bytes(),
SendShipPacket::DoneLoadingQuest(pkt) => pkt.as_bytes(), SendShipPacket::DoneLoadingQuest(pkt) => pkt.as_bytes(),
SendShipPacket::BankItemList(pkt) => pkt.as_bytes(), SendShipPacket::BankItemList(pkt) => pkt.as_bytes(),
SendShipPacket::RedirectClient(pkt) => pkt.as_bytes(),
} }
} }
} }
@ -231,7 +243,7 @@ pub struct ClientState {
pub character: CharacterEntity, pub character: CharacterEntity,
session: Session, session: Session,
//guildcard: GuildCard, //guildcard: GuildCard,
pub block: u32,
pub block: usize,
pub item_drop_location: Option<ItemDropLocation>, pub item_drop_location: Option<ItemDropLocation>,
pub done_loading_quest: bool, pub done_loading_quest: bool,
//pub loading_quest: Option<LoadingQuest>, //pub loading_quest: Option<LoadingQuest>,
@ -251,7 +263,7 @@ impl ClientState {
settings: settings, settings: settings,
character: character, character: character,
session: session, session: session,
block: 1,
block: 0,
item_drop_location: None, item_drop_location: None,
done_loading_quest: false, done_loading_quest: false,
area: None, area: None,
@ -299,6 +311,7 @@ pub struct ShipServerStateBuilder<EG: EntityGateway> {
ip: Option<Ipv4Addr>, ip: Option<Ipv4Addr>,
port: Option<u16>, port: Option<u16>,
auth_token: Option<AuthToken>, auth_token: Option<AuthToken>,
num_blocks: usize,
} }
impl<EG: EntityGateway> ShipServerStateBuilder<EG> { impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
@ -309,6 +322,7 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
ip: None, ip: None,
port: None, port: None,
auth_token: None, auth_token: None,
num_blocks: 2,
} }
} }
@ -337,37 +351,73 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
self self
} }
pub fn blocks(mut self, num_blocks: usize) -> ShipServerStateBuilder<EG> {
self.num_blocks = num_blocks;
self
}
pub fn build(self) -> ShipServerState<EG> { pub fn build(self) -> ShipServerState<EG> {
let blocks = std::iter::repeat_with(Block::default).take(self.num_blocks).collect(); // Block doesn't have a Clone impl which limits the easy ways to init this
ShipServerState { ShipServerState {
entity_gateway: self.entity_gateway.unwrap(), entity_gateway: self.entity_gateway.unwrap(),
clients: HashMap::new(), clients: HashMap::new(),
client_location: Box::new(ClientLocation::new()),
level_table: CharacterLevelTable::new(), level_table: CharacterLevelTable::new(),
name: self.name.unwrap_or("NAMENOTSET".into()), name: self.name.unwrap_or("NAMENOTSET".into()),
rooms: Box::new([None; MAX_ROOMS]),
item_manager: items::ItemManager::new(), item_manager: items::ItemManager::new(),
quests: quests::load_quests("data/quests.toml".into()).unwrap(), quests: quests::load_quests("data/quests.toml".into()).unwrap(),
ip: self.ip.unwrap_or(Ipv4Addr::new(127,0,0,1)), ip: self.ip.unwrap_or(Ipv4Addr::new(127,0,0,1)),
port: self.port.unwrap_or(SHIP_PORT), port: self.port.unwrap_or(SHIP_PORT),
shops: Box::new(ItemShops::new()), shops: Box::new(ItemShops::new()),
blocks: Blocks(blocks),
auth_token: self.auth_token.unwrap_or(AuthToken("".into())), auth_token: self.auth_token.unwrap_or(AuthToken("".into())),
ship_list: Vec::new(),
shipgate_sender: None,
}
}
} }
pub struct Block {
client_location: Box<ClientLocation>,
pub rooms: Box<Rooms>,
}
impl Default for Block {
fn default() -> Block {
Block {
client_location: Box::new(ClientLocation::default()),
rooms: Box::new([None; MAX_ROOMS]),
}
}
}
pub struct Blocks(pub Vec<Block>);
impl Blocks {
fn with_client(&mut self, id: ClientId, clients: &Clients) -> Result<&mut Block, ShipError> {
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
self.0.get_mut(client.block).ok_or(ShipError::InvalidBlock(client.block))
} }
} }
pub struct ShipServerState<EG: EntityGateway> { pub struct ShipServerState<EG: EntityGateway> {
entity_gateway: EG, entity_gateway: EG,
pub clients: Clients, pub clients: Clients,
client_location: Box<ClientLocation>,
level_table: CharacterLevelTable, level_table: CharacterLevelTable,
name: String, name: String,
pub rooms: Box<Rooms>,
item_manager: items::ItemManager, item_manager: items::ItemManager,
quests: quests::QuestList, quests: quests::QuestList,
shops: Box<ItemShops>,
pub blocks: Blocks,
ip: Ipv4Addr, ip: Ipv4Addr,
port: u16, port: u16,
shops: Box<ItemShops>,
auth_token: AuthToken, auth_token: AuthToken,
ship_list: Vec<Ship>,
shipgate_sender: Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
} }
impl<EG: EntityGateway> ShipServerState<EG> { impl<EG: EntityGateway> ShipServerState<EG> {
@ -375,37 +425,48 @@ impl<EG: EntityGateway> ShipServerState<EG> {
ShipServerStateBuilder::new() ShipServerStateBuilder::new()
} }
pub fn set_sender(&mut self, sender: Box<dyn Fn(ShipMessage) + Send + Sync>) {
self.shipgate_sender = Some(sender);
}
async fn message(&mut self, id: ClientId, msg: &Message) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> { async fn message(&mut self, id: ClientId, msg: &Message) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
Ok(match &msg.msg { Ok(match &msg.msg {
GameMessage::RequestExp(request_exp) => { GameMessage::RequestExp(request_exp) => {
handler::message::request_exp(id, request_exp, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.rooms, &self.level_table).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::message::request_exp(id, request_exp, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &self.level_table).await?
}, },
GameMessage::PlayerDropItem(player_drop_item) => { GameMessage::PlayerDropItem(player_drop_item) => {
handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await?
}, },
GameMessage::DropCoordinates(drop_coordinates) => { GameMessage::DropCoordinates(drop_coordinates) => {
handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms)?
let block = self.blocks.with_client(id, &self.clients)?;
handler::message::drop_coordinates(id, drop_coordinates, &block.client_location, &mut self.clients, &block.rooms)?
}, },
GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => { GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => {
handler::message::split_item_stack(id, no_longer_has_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::message::split_item_stack(id, no_longer_has_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await?
}, },
GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) | GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) |
GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) | GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) |
GameMessage::PlayerLoadedIn(_) | GameMessage::PlayerWalking(_) | GameMessage::PlayerRunning(_) | GameMessage::PlayerLoadedIn(_) | GameMessage::PlayerWalking(_) | GameMessage::PlayerRunning(_) |
GameMessage::PlayerWarped(_) | GameMessage::PlayerChangedFloor(_) | GameMessage::InitializeSpeechNpc(_) => { GameMessage::PlayerWarped(_) | GameMessage::PlayerChangedFloor(_) | GameMessage::InitializeSpeechNpc(_) => {
handler::message::update_player_position(id, &msg, &mut self.clients, &mut self.client_location, &self.rooms)?
let block = self.blocks.with_client(id, &self.clients)?;
handler::message::update_player_position(id, &msg, &mut self.clients, &mut block.client_location, &block.rooms)?
}, },
GameMessage::ChargeAttack(charge_attack) => { GameMessage::ChargeAttack(charge_attack) => {
handler::message::charge_attack(id, charge_attack, &mut self.clients, &mut self.entity_gateway).await? handler::message::charge_attack(id, charge_attack, &mut self.clients, &mut self.entity_gateway).await?
}, },
GameMessage::PlayerUseItem(player_use_item) => { GameMessage::PlayerUseItem(player_use_item) => {
handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await?
}, },
GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => { GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => {
handler::message::player_used_medical_center(id, &player_used_medical_center, &mut self.entity_gateway, &mut self.clients).await? handler::message::player_used_medical_center(id, &player_used_medical_center, &mut self.entity_gateway, &mut self.clients).await?
}, },
GameMessage::PlayerFeedMag(player_feed_mag) => { GameMessage::PlayerFeedMag(player_feed_mag) => {
handler::message::player_feed_mag(id, &player_feed_mag, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::message::player_feed_mag(id, &player_feed_mag, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await?
}, },
GameMessage::PlayerEquipItem(player_equip_item) => { GameMessage::PlayerEquipItem(player_equip_item) => {
handler::message::player_equips_item(id, &player_equip_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? handler::message::player_equips_item(id, &player_equip_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
@ -418,7 +479,8 @@ impl<EG: EntityGateway> ShipServerState<EG> {
}, },
_ => { _ => {
let cmsg = msg.clone(); let cmsg = msg.clone();
Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter()
let block = self.blocks.with_client(id, &self.clients)?;
Box::new(block.client_location.get_client_neighbors(id).unwrap().into_iter()
.map(move |client| { .map(move |client| {
(client.client, SendShipPacket::Message(cmsg.clone())) (client.client, SendShipPacket::Message(cmsg.clone()))
})) }))
@ -430,32 +492,40 @@ impl<EG: EntityGateway> ShipServerState<EG> {
let target = msg.flag; let target = msg.flag;
Ok(match &msg.msg { Ok(match &msg.msg {
GameMessage::GuildcardSend(guildcard_send) => { GameMessage::GuildcardSend(guildcard_send) => {
handler::direct_message::guildcard_send(id, guildcard_send, target, &self.client_location, &self.clients)
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients)
}, },
GameMessage::RequestItem(request_item) => { GameMessage::RequestItem(request_item) => {
handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await?
}, },
GameMessage::PickupItem(pickup_item) => { GameMessage::PickupItem(pickup_item) => {
handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await?
}, },
GameMessage::BoxDropRequest(box_drop_request) => { GameMessage::BoxDropRequest(box_drop_request) => {
handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await?
}, },
GameMessage::BankRequest(_bank_request) => { GameMessage::BankRequest(_bank_request) => {
handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await? handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await?
}, },
GameMessage::BankInteraction(bank_interaction) => { GameMessage::BankInteraction(bank_interaction) => {
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.item_manager).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
}, },
GameMessage::ShopRequest(shop_request) => { GameMessage::ShopRequest(shop_request) => {
handler::direct_message::shop_request(id, shop_request, &self.client_location, &mut self.clients, &self.rooms, &self.level_table, &mut self.shops).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await?
}, },
GameMessage::BuyItem(buy_item) => { GameMessage::BuyItem(buy_item) => {
handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.item_manager).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
}, },
_ => { _ => {
let cmsg = msg.clone(); let cmsg = msg.clone();
Box::new(self.client_location.get_all_clients_by_client(id).unwrap().into_iter()
let block = self.blocks.with_client(id, &self.clients)?;
Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter()
.filter(move |client| client.local_client.id() == target as u8) .filter(move |client| client.local_client.id() == target as u8)
.map(move |client| { .map(move |client| {
(client.client, SendShipPacket::DirectMessage(cmsg.clone())) (client.client, SendShipPacket::DirectMessage(cmsg.clone()))
@ -489,7 +559,8 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> { -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
Ok(match pkt { Ok(match pkt {
RecvShipPacket::Login(login) => { 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, self.blocks.0.len())
.await?.into_iter().map(move |pkt| (id, pkt)))
}, },
RecvShipPacket::QuestDetailRequest(questdetailrequest) => { RecvShipPacket::QuestDetailRequest(questdetailrequest) => {
match questdetailrequest.menu { match questdetailrequest.menu {
@ -498,36 +569,49 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
} }
}, },
RecvShipPacket::MenuSelect(menuselect) => { RecvShipPacket::MenuSelect(menuselect) => {
let block = self.blocks.with_client(id, &self.clients)?;
match menuselect.menu { match menuselect.menu {
BLOCK_MENU_ID => Box::new(handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_manager, &self.level_table)?.into_iter().map(move |pkt| (id, pkt))),
ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut self.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut self.rooms)?,
SHIP_MENU_ID => {
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten();
let select_ship = handler::ship::selected_ship(id, menuselect, &self.ship_list)?;
Box::new(leave_lobby.chain(select_ship))
}
BLOCK_MENU_ID => {
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten();
let select_block = handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_manager, &self.level_table)?.into_iter();
Box::new(leave_lobby.chain(select_block))
}
ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)?,
QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &self.quests)?, QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &self.quests)?,
_ => unreachable!(), _ => unreachable!(),
} }
}, },
RecvShipPacket::QuestMenuSelect(questmenuselect) => { RecvShipPacket::QuestMenuSelect(questmenuselect) => {
handler::quest::load_quest(id, questmenuselect, &self.quests, &mut self.clients, &self.client_location, &mut self.rooms)?
let block = self.blocks.with_client(id, &self.clients)?;
handler::quest::load_quest(id, questmenuselect, &self.quests, &mut self.clients, &block.client_location, &mut block.rooms)?
}, },
RecvShipPacket::MenuDetail(_menudetail) => { RecvShipPacket::MenuDetail(_menudetail) => {
//unreachable!(); //unreachable!();
Box::new(Vec::new().into_iter()) Box::new(Vec::new().into_iter())
}, },
RecvShipPacket::RoomPasswordReq(room_password_req) => { RecvShipPacket::RoomPasswordReq(room_password_req) => {
if room_password_req.password == self.rooms[room_password_req.item as usize].as_ref()
let block = self.blocks.with_client(id, &self.clients)?;
if room_password_req.password == block.rooms[room_password_req.item as usize].as_ref()
.ok_or(ShipError::InvalidRoom(room_password_req.item))? .ok_or(ShipError::InvalidRoom(room_password_req.item))?
.password { .password {
let menuselect = MenuSelect { let menuselect = MenuSelect {
menu: room_password_req.menu, menu: room_password_req.menu,
item: room_password_req.item, item: room_password_req.item,
}; };
handler::room::join_room(id, &menuselect, &mut self.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut self.rooms)?
handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)?
} }
else { else {
Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))].into_iter()) Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))].into_iter())
} }
}, },
RecvShipPacket::CharData(chardata) => { RecvShipPacket::CharData(chardata) => {
Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut self.client_location, &self.clients, &self.item_manager, &self.level_table)?.into_iter())
let block = self.blocks.with_client(id, &self.clients)?;
Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_manager, &self.level_table)?.into_iter())
}, },
RecvShipPacket::Message(msg) => { RecvShipPacket::Message(msg) => {
self.message(id, msg).await? self.message(id, msg).await?
@ -536,41 +620,50 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
self.direct_message(id, msg).await? self.direct_message(id, msg).await?
}, },
RecvShipPacket::PlayerChat(msg) => { RecvShipPacket::PlayerChat(msg) => {
Box::new(handler::communication::player_chat(id, msg, &self.client_location, &self.clients)?.into_iter())
let block = self.blocks.with_client(id, &self.clients)?;
Box::new(handler::communication::player_chat(id, msg, &block.client_location, &self.clients)?.into_iter())
}, },
RecvShipPacket::CreateRoom(create_room) => { RecvShipPacket::CreateRoom(create_room) => {
handler::room::create_room(id, create_room, &mut self.client_location, &mut self.clients, &mut self.item_manager, &mut self.rooms)?
let block = self.blocks.with_client(id, &self.clients)?;
handler::room::create_room(id, create_room, &mut block.client_location, &mut self.clients, &mut self.item_manager, &mut block.rooms)?
}, },
RecvShipPacket::RoomNameRequest(_req) => { RecvShipPacket::RoomNameRequest(_req) => {
handler::room::room_name_request(id, &self.client_location, &self.rooms)
let block = self.blocks.with_client(id, &self.clients)?;
handler::room::room_name_request(id, &block.client_location, &block.rooms)
}, },
RecvShipPacket::UpdateConfig(pkt) => { RecvShipPacket::UpdateConfig(pkt) => {
handler::settings::update_config(id, pkt, &mut self.clients, &mut self.entity_gateway).await handler::settings::update_config(id, pkt, &mut self.clients, &mut self.entity_gateway).await
}, },
RecvShipPacket::ViewInfoboardRequest(_pkt) => { RecvShipPacket::ViewInfoboardRequest(_pkt) => {
handler::communication::request_infoboard(id, &self.client_location, &self.clients)
let block = self.blocks.with_client(id, &self.clients)?;
handler::communication::request_infoboard(id, &block.client_location, &self.clients)
}, },
RecvShipPacket::WriteInfoboard(pkt) => { RecvShipPacket::WriteInfoboard(pkt) => {
handler::communication::write_infoboard(id, pkt, &mut self.clients, &mut self.entity_gateway).await handler::communication::write_infoboard(id, pkt, &mut self.clients, &mut self.entity_gateway).await
}, },
RecvShipPacket::RoomListRequest(_req) => { RecvShipPacket::RoomListRequest(_req) => {
handler::room::request_room_list(id, &self.client_location, &self.rooms)
let block = self.blocks.with_client(id, &self.clients)?;
handler::room::request_room_list(id, &block.client_location, &block.rooms)
}, },
RecvShipPacket::Like62ButCooler(cool62) => { RecvShipPacket::Like62ButCooler(cool62) => {
handler::room::cool_62(id, cool62, &self.client_location)
let block = self.blocks.with_client(id, &self.clients)?;
handler::room::cool_62(id, cool62, &block.client_location)
}, },
RecvShipPacket::ClientCharacterData(_) => { RecvShipPacket::ClientCharacterData(_) => {
// TOOD: validate this in some way? // TOOD: validate this in some way?
Box::new(None.into_iter()) Box::new(None.into_iter())
}, },
RecvShipPacket::DoneBursting(_) => { RecvShipPacket::DoneBursting(_) => {
handler::room::done_bursting(id, &self.client_location, &mut self.rooms)
let block = self.blocks.with_client(id, &self.clients)?;
handler::room::done_bursting(id, &block.client_location, &mut block.rooms)
}, },
RecvShipPacket::DoneBursting2(_) => { RecvShipPacket::DoneBursting2(_) => {
handler::room::done_bursting(id, &self.client_location, &mut self.rooms)
let block = self.blocks.with_client(id, &self.clients)?;
handler::room::done_bursting(id, &block.client_location, &mut block.rooms)
}, },
RecvShipPacket::LobbySelect(pkt) => { RecvShipPacket::LobbySelect(pkt) => {
Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut self.client_location, &self.clients, &mut self.item_manager, &self.level_table, &mut self.rooms, &mut self.entity_gateway).await?.into_iter())
let block = self.blocks.with_client(id, &self.clients)?;
Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms, &mut self.entity_gateway).await?.into_iter())
}, },
RecvShipPacket::RequestQuestList(_) => { RecvShipPacket::RequestQuestList(_) => {
handler::quest::send_quest_category_list(id, &self.quests)? handler::quest::send_quest_category_list(id, &self.quests)?
@ -582,7 +675,8 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
handler::quest::quest_chunk_ack(id, quest_chunk_ack, &self.quests)? handler::quest::quest_chunk_ack(id, quest_chunk_ack, &self.quests)?
}, },
RecvShipPacket::DoneLoadingQuest(_) => { RecvShipPacket::DoneLoadingQuest(_) => {
handler::quest::done_loading_quest(id, &mut self.clients, &self.client_location)?
let block = self.blocks.with_client(id, &self.clients)?;
handler::quest::done_loading_quest(id, &mut self.clients, &block.client_location)?
}, },
RecvShipPacket::FullCharacterData(_full_character_data) => { RecvShipPacket::FullCharacterData(_full_character_data) => {
Box::new(None.into_iter()) Box::new(None.into_iter())
@ -590,30 +684,40 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
RecvShipPacket::SaveOptions(save_options) => { RecvShipPacket::SaveOptions(save_options) => {
handler::settings::save_options(id, save_options, &mut self.clients, &mut self.entity_gateway).await handler::settings::save_options(id, save_options, &mut self.clients, &mut self.entity_gateway).await
}, },
RecvShipPacket::RequestShipList(_) => {
handler::ship::ship_list(id, &self.ship_list)
},
RecvShipPacket::RequestShipBlockList(_) => {
handler::ship::block_list(id, &self.name, self.blocks.0.len())
}
}) })
} }
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> { async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
// TODO: don't unwrap!
let client = self.clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; 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)?;
let block = self.blocks.with_client(id, &self.clients)?;
let area_client = block.client_location.get_local_client(id)?;
let neighbors = block.client_location.get_client_neighbors(id)?;
let pkt = match self.client_location.get_area(id)? {
let pkt = match block.client_location.get_area(id)? {
RoomLobby::Room(room) => { RoomLobby::Room(room) => {
if neighbors.len() == 0 { if neighbors.len() == 0 {
self.rooms[room.0] = None;
block.rooms[room.0] = None;
} }
let leader = self.client_location.get_room_leader(room).unwrap();
let leader = block.client_location.get_room_leader(room)?;
SendShipPacket::LeaveRoom(LeaveRoom::new(area_client.local_client.id(), leader.local_client.id())) SendShipPacket::LeaveRoom(LeaveRoom::new(area_client.local_client.id(), leader.local_client.id()))
}, },
RoomLobby::Lobby(lobby) => { RoomLobby::Lobby(lobby) => {
let leader = self.client_location.get_lobby_leader(lobby).unwrap();
let leader = block.client_location.get_lobby_leader(lobby)?;
SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id())) SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()))
} }
}; };
self.client_location.remove_client_from_area(id);
if let Some(shipgate_sender) = self.shipgate_sender.as_ref() {
shipgate_sender(ShipMessage::RemoveUser(client.user.id.clone()));
}
block.client_location.remove_client_from_area(id);
self.item_manager.remove_character_from_room(&client.character); self.item_manager.remove_character_from_room(&client.character);
if let Some(mut client) = self.clients.remove(&id) { if let Some(mut client) = self.clients.remove(&id) {
@ -642,11 +746,28 @@ impl<EG: EntityGateway> InterserverActor for ShipServerState<EG> {
ip: self.ip.clone(), ip: self.ip.clone(),
port: self.port, port: self.port,
block_count: 2, block_count: 2,
})) ]
})),
(id, ShipMessage::RequestShipList)
]
} }
async fn action(&mut self, _id: ServerId, _msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
match msg {
LoginMessage::SendMail{..} => {
Ok(Vec::new())
},
LoginMessage::ShipList{ships} => {
self.ship_list = ships;
Ok(Vec::new()) 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)> { async fn on_disconnect(&mut self, _id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {

8
tests/test_exp_gain.rs

@ -25,7 +25,7 @@ async fn test_character_gains_exp() {
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
let (enemy_id, exp) = { let (enemy_id, exp) = {
let room = ship.rooms[0].as_ref().unwrap();
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
let (enemy_id, map_enemy) = (0..).filter_map(|i| { let (enemy_id, map_enemy) = (0..).filter_map(|i| {
room.maps.enemy_by_id(i).map(|enemy| { room.maps.enemy_by_id(i).map(|enemy| {
(i, enemy) (i, enemy)
@ -64,7 +64,7 @@ async fn test_character_levels_up() {
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
let enemy_id = { let enemy_id = {
let room = ship.rooms[0].as_ref().unwrap();
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
(0..).filter_map(|i| { (0..).filter_map(|i| {
room.maps.enemy_by_id(i).map(|_| { room.maps.enemy_by_id(i).map(|_| {
i i
@ -102,7 +102,7 @@ async fn test_character_levels_up_multiple_times() {
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
let (enemy_id, exp) = { let (enemy_id, exp) = {
let room = ship.rooms[0].as_ref().unwrap();
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
let (enemy_id, map_enemy) = (0..).filter_map(|i| { let (enemy_id, map_enemy) = (0..).filter_map(|i| {
room.maps.enemy_by_id(i).ok().and_then(|enemy| { room.maps.enemy_by_id(i).ok().and_then(|enemy| {
if enemy.monster == MonsterType::DarkFalz2 { if enemy.monster == MonsterType::DarkFalz2 {
@ -152,7 +152,7 @@ async fn test_one_character_gets_full_exp_and_other_attacker_gets_partial() {
join_room(&mut ship, ClientId(2), 0).await; join_room(&mut ship, ClientId(2), 0).await;
let (enemy_id, exp) = { let (enemy_id, exp) = {
let room = ship.rooms[0].as_ref().unwrap();
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
let (enemy_id, map_enemy) = (0..).filter_map(|i| { let (enemy_id, map_enemy) = (0..).filter_map(|i| {
room.maps.enemy_by_id(i).ok().and_then(|enemy| { room.maps.enemy_by_id(i).ok().and_then(|enemy| {
if enemy.monster == MonsterType::DarkFalz2 { if enemy.monster == MonsterType::DarkFalz2 {

Loading…
Cancel
Save