Merge pull request 'misc shit' (#246) from cleaning_up into master
This commit is contained in:
commit
898cb9064d
@ -34,4 +34,5 @@ refinery = { version = "0.3.0", features = ["postgres"] }
|
||||
sqlx = { version = "0.4.0-beta.1", features = ["postgres", "json", "chrono"] }
|
||||
strum = "0.19.5"
|
||||
strum_macros = "0.19"
|
||||
anyhow = "1.0.33"
|
||||
|
||||
|
@ -3,6 +3,7 @@ use elseware::entity::gateway::postgres::PostgresGateway;
|
||||
use elseware::login::login::LoginServerState;
|
||||
use elseware::login::character::CharacterServerState;
|
||||
use elseware::common::mainloop::{login_mainloop, character_mainloop};
|
||||
use elseware::common::interserver::AuthToken;
|
||||
|
||||
fn main() {
|
||||
let colors = fern::colors::ColoredLevelConfig::new()
|
||||
@ -33,13 +34,14 @@ fn main() {
|
||||
let db_password = std::env::var("DB_PASSWORD").unwrap();
|
||||
let db_dbname = std::env::var("DB_DBNAME").unwrap();
|
||||
let charserv_ip = std::env::var("CHARSERV_IP").unwrap().parse().unwrap();
|
||||
let shipgate_token = std::env::var("SHIPGATE_TOKEN").unwrap();
|
||||
let entity_gateway = PostgresGateway::new(&db_host, &db_dbname, &db_username, &db_password);
|
||||
|
||||
let thread_entity_gateway = entity_gateway.clone();
|
||||
let login_state = LoginServerState::new(thread_entity_gateway, charserv_ip);
|
||||
let login_loop = login_mainloop(login_state, elseware::login::login::LOGIN_PORT);
|
||||
|
||||
let char_state = CharacterServerState::new(entity_gateway);
|
||||
let char_state = CharacterServerState::new(entity_gateway, AuthToken(shipgate_token.into()));
|
||||
let character_loop = character_mainloop(char_state, elseware::login::character::CHARACTER_PORT, elseware::login::login::COMMUNICATION_PORT);
|
||||
|
||||
info!("[auth/character] starting server");
|
||||
|
@ -9,6 +9,7 @@ use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
|
||||
use elseware::entity::gateway::{EntityGateway, InMemoryGateway, PostgresGateway};
|
||||
use elseware::entity::character::NewCharacterEntity;
|
||||
use elseware::entity::item::{NewItemEntity, ItemDetail, ItemLocation};
|
||||
use elseware::common::interserver::AuthToken;
|
||||
|
||||
use elseware::entity::item;
|
||||
use elseware::common::mainloop::*;
|
||||
@ -405,7 +406,7 @@ fn main() {
|
||||
|
||||
let thread_entity_gateway = entity_gateway.clone();
|
||||
info!("[character] starting server");
|
||||
let char_state = CharacterServerState::new(thread_entity_gateway);
|
||||
let char_state = CharacterServerState::new(thread_entity_gateway, AuthToken("".into()));
|
||||
let character_loop = character_mainloop(char_state, elseware::login::character::CHARACTER_PORT, elseware::login::login::COMMUNICATION_PORT);
|
||||
|
||||
let thread_entity_gateway = entity_gateway.clone();
|
||||
|
@ -2,6 +2,7 @@ use log::{info};
|
||||
use elseware::entity::gateway::postgres::PostgresGateway;
|
||||
use elseware::ship::ship::ShipServerStateBuilder;
|
||||
use elseware::common::mainloop::ship_mainloop;
|
||||
use elseware::common::interserver::AuthToken;
|
||||
|
||||
fn main() {
|
||||
let colors = fern::colors::ColoredLevelConfig::new()
|
||||
@ -33,6 +34,7 @@ fn main() {
|
||||
let db_dbname = std::env::var("DB_DBNAME").unwrap();
|
||||
let entity_gateway = PostgresGateway::new(&db_host, &db_dbname, &db_username, &db_password);
|
||||
|
||||
let shipgate_token = std::env::var("SHIPGATE_TOKEN").unwrap();
|
||||
let ship_name = std::env::var("SHIP_NAME").unwrap().parse().unwrap();
|
||||
let ip = std::env::var("SELF_IP").unwrap().parse().unwrap();
|
||||
let ship_state = ShipServerStateBuilder::new()
|
||||
@ -40,6 +42,7 @@ fn main() {
|
||||
.ip(ip)
|
||||
.port(elseware::ship::ship::SHIP_PORT)
|
||||
.gateway(entity_gateway)
|
||||
.auth_token(AuthToken(shipgate_token.into()))
|
||||
.build();
|
||||
|
||||
let shipgate_ip = std::env::var("SHIPGATE_IP").unwrap().parse().unwrap();
|
||||
|
@ -5,7 +5,7 @@ use crate::entity::character::CharacterEntityId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ServerId(pub usize);
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct AuthToken(pub String);
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -65,7 +65,7 @@ pub trait EntityGateway: Send + Sync + Clone {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
async fn save_item(&mut self, _item: &ItemEntity) -> Result<(), GatewayError> {
|
||||
async fn change_item(&mut self, _id: &ItemEntityId, _item: &ItemDetail) -> Result<(), GatewayError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
|
@ -165,9 +165,12 @@ impl EntityGateway for InMemoryGateway {
|
||||
Ok(new_item)
|
||||
}
|
||||
|
||||
async fn save_item(&mut self, item: &ItemEntity) -> Result<(), GatewayError> {
|
||||
async fn change_item(&mut self, id: &ItemEntityId, item: &ItemDetail) -> Result<(), GatewayError> {
|
||||
let mut items = self.items.lock().unwrap();
|
||||
items.insert(item.id, item.clone());
|
||||
if let Some((_, ref mut old_item)) = items.iter_mut().find(|(existing_id, _)| **existing_id == *id) {
|
||||
old_item.item = item.clone();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -312,6 +312,14 @@ impl EntityGateway for PostgresGateway {
|
||||
})
|
||||
}
|
||||
|
||||
async fn change_item(&mut self, id: &ItemEntityId, item: &ItemDetail) -> Result<(), GatewayError> {
|
||||
sqlx::query("update item set item = $1 where id = $2")
|
||||
.bind(sqlx::types::Json(PgItemDetail::from(item.clone())))
|
||||
.bind(id.0)
|
||||
.execute(&self.pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> {
|
||||
let mut tx = self.pool.begin().await?;
|
||||
if let ItemLocation::Inventory{slot, ..} = &item_location {
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![allow(dead_code, unused_assignments)]
|
||||
use std::io::Read;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
|
||||
use rand::Rng;
|
||||
use crc::{crc32, Hasher32};
|
||||
@ -28,11 +28,13 @@ use crate::entity::item::mag::Mag;
|
||||
use crate::entity::character::{CharacterEntity, NewCharacterEntity, CharacterClass, TechLevel};
|
||||
|
||||
use crate::login::login::{get_login_status, check_if_already_online};
|
||||
use crate::common::interserver::AuthToken;
|
||||
|
||||
pub const CHARACTER_PORT: u16 = 12001;
|
||||
const SHIP_MENU_ID: u32 = 1;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error("")]
|
||||
pub enum CharacterError {
|
||||
InvalidMenuSelection(u32, u32),
|
||||
ClientNotFound(ClientId),
|
||||
@ -174,6 +176,8 @@ pub struct CharacterServerState<EG: EntityGateway> {
|
||||
clients: HashMap<ClientId, ClientState>,
|
||||
ships: BTreeMap<ServerId, Ship>,
|
||||
level_table: CharacterLevelTable,
|
||||
auth_token: AuthToken,
|
||||
authenticated_ships: BTreeSet<ServerId>,
|
||||
}
|
||||
|
||||
|
||||
@ -266,7 +270,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
|
||||
|
||||
|
||||
impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
pub fn new(entity_gateway: EG) -> CharacterServerState<EG> {
|
||||
pub fn new(entity_gateway: EG, auth_token: AuthToken) -> CharacterServerState<EG> {
|
||||
let (param_header, param_data) = generate_param_data("data/param/");
|
||||
|
||||
CharacterServerState {
|
||||
@ -276,10 +280,12 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
clients: HashMap::new(),
|
||||
ships: BTreeMap::new(),
|
||||
level_table: CharacterLevelTable::new(),
|
||||
auth_token: auth_token,
|
||||
authenticated_ships: BTreeSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
||||
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) => {
|
||||
@ -299,7 +305,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
})
|
||||
}
|
||||
|
||||
fn send_ship_list(&mut self, _id: ClientId, _pkt: &Login) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
||||
fn send_ship_list(&mut self, _id: ClientId, _pkt: &Login) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
Ok(vec![SendCharacterPacket::Timestamp(Timestamp::new(chrono::Utc::now())),
|
||||
SendCharacterPacket::ShipList(ShipList::new(self.ships.iter().map(|(i, s)| {
|
||||
ShipListEntry {
|
||||
@ -312,7 +318,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
])
|
||||
}
|
||||
|
||||
async fn get_settings(&mut self, id: ClientId) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
||||
async fn get_settings(&mut self, id: ClientId) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
||||
let user = client.user.as_ref().unwrap();
|
||||
|
||||
@ -331,7 +337,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
Ok(vec![pkt])
|
||||
}
|
||||
|
||||
async fn char_select(&mut self, id: ClientId, select: &CharSelect) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
||||
async fn char_select(&mut self, id: ClientId, select: &CharSelect) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
||||
if client.characters.is_none() {
|
||||
client.characters = Some(self.entity_gateway.get_characters_by_user(client.user.as_ref().unwrap()).await.map_err(|_| CharacterError::CouldNotLoadCharacters)?);
|
||||
@ -377,7 +383,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
})]
|
||||
}
|
||||
|
||||
async fn guildcard_data_header(&mut self, id: ClientId) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
||||
async fn guildcard_data_header(&mut self, id: ClientId) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
||||
let guildcard_data = self.entity_gateway.get_guild_card_data_by_user(client.user.as_ref().unwrap()).await.map_err(|_| CharacterError::CouldNotLoadGuildcard)?;
|
||||
|
||||
@ -389,7 +395,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
Ok(vec![SendCharacterPacket::GuildcardDataHeader(GuildcardDataHeader::new(bytes.len(), crc.sum32()))])
|
||||
}
|
||||
|
||||
fn guildcard_data_chunk(&mut self, id: ClientId, chunk: u32, again: u32) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
||||
fn guildcard_data_chunk(&mut self, id: ClientId, chunk: u32, again: u32) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
||||
Ok(if again != 0 {
|
||||
let start = chunk as usize * GUILD_CARD_CHUNK_SIZE;
|
||||
@ -405,7 +411,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
})
|
||||
}
|
||||
|
||||
async fn set_flag(&mut self, id: ClientId, setflag: &SetFlag) -> Result<std::option::IntoIter<SendCharacterPacket>, CharacterError> {
|
||||
async fn set_flag(&mut self, id: ClientId, setflag: &SetFlag) -> Result<std::option::IntoIter<SendCharacterPacket>, anyhow::Error> {
|
||||
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
||||
let mut user = client.user.as_mut().unwrap();
|
||||
user.flags = setflag.flags;
|
||||
@ -413,7 +419,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
Ok(None.into_iter())
|
||||
}
|
||||
|
||||
fn param_data_chunk_request(&mut self, id: ClientId, _request: &ParamDataChunkRequest) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
||||
fn param_data_chunk_request(&mut self, id: ClientId, _request: &ParamDataChunkRequest) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
||||
let chunk = client.param_index;
|
||||
client.param_index += 1;
|
||||
@ -434,7 +440,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
|
||||
|
||||
// TODO: move USERFLAGS over to SessionAction
|
||||
async fn character_preview(&mut self, id: ClientId, preview: &CharacterPreview) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
||||
async fn character_preview(&mut self, id: ClientId, preview: &CharacterPreview) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
||||
let mut user = client.user.as_mut().unwrap();
|
||||
if user.flags == USERFLAG_NEWCHAR {
|
||||
@ -458,9 +464,9 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
])
|
||||
}
|
||||
|
||||
fn select_ship(&mut self, menuselect: &MenuSelect) -> Result<Vec<SendCharacterPacket>, CharacterError> {
|
||||
fn select_ship(&mut self, menuselect: &MenuSelect) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
if menuselect.menu != SHIP_MENU_ID {
|
||||
return Err(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item));
|
||||
Err(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?;
|
||||
}
|
||||
|
||||
let ship = self.ships.get(&ServerId(menuselect.item as usize))
|
||||
@ -473,9 +479,9 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
|
||||
type SendPacket = SendCharacterPacket;
|
||||
type RecvPacket = RecvCharacterPacket;
|
||||
type PacketError = CharacterError;
|
||||
type PacketError = anyhow::Error;
|
||||
|
||||
async fn on_connect(&mut self, id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, CharacterError> {
|
||||
async fn on_connect(&mut self, id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, anyhow::Error> {
|
||||
self.clients.insert(id, ClientState::new());
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
@ -492,7 +498,7 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
|
||||
}
|
||||
|
||||
async fn handle(&mut self, id: ClientId, pkt: &RecvCharacterPacket)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendCharacterPacket)> + Send>, CharacterError> {
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendCharacterPacket)> + Send>, anyhow::Error> {
|
||||
Ok(match pkt {
|
||||
RecvCharacterPacket::Login(login) => {
|
||||
if login.session.action == SessionAction::SelectCharacter {
|
||||
@ -535,7 +541,7 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
|
||||
})
|
||||
}
|
||||
|
||||
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendCharacterPacket)>, CharacterError> {
|
||||
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(mut user) = client.user {
|
||||
user.at_character= false;
|
||||
@ -558,9 +564,15 @@ impl<EG: EntityGateway> InterserverActor for CharacterServerState<EG> {
|
||||
|
||||
async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
|
||||
match msg {
|
||||
ShipMessage::Authenticate(_auth_token) => {},
|
||||
ShipMessage::Authenticate(auth_token) => {
|
||||
if self.auth_token == auth_token {
|
||||
self.authenticated_ships.insert(id);
|
||||
}
|
||||
},
|
||||
ShipMessage::NewShip(new_ship) => {
|
||||
self.ships.insert(id, new_ship);
|
||||
if self.authenticated_ships.contains(&id) {
|
||||
self.ships.insert(id, new_ship);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
@ -681,7 +693,7 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
let mut server = CharacterServerState::new(TestData {});
|
||||
let mut server = CharacterServerState::new(TestData {}, AuthToken("".into()));
|
||||
let mut clientstate = ClientState::new();
|
||||
clientstate.user = Some(UserAccountEntity {
|
||||
id: UserAccountId(1),
|
||||
@ -716,7 +728,7 @@ mod test {
|
||||
#[derive(Clone)]
|
||||
struct TestData;
|
||||
impl EntityGateway for TestData {}
|
||||
let mut server = CharacterServerState::new(TestData {});
|
||||
let mut server = CharacterServerState::new(TestData {}, AuthToken("".into()));
|
||||
let send = server.handle(ClientId(1), &RecvCharacterPacket::Checksum(Checksum {checksum: 1234,
|
||||
padding: 0,
|
||||
})).await.unwrap().collect::<Vec<_>>();
|
||||
@ -746,7 +758,7 @@ mod test {
|
||||
at_ship: false,
|
||||
});
|
||||
|
||||
let mut server = CharacterServerState::new(test_data.clone());
|
||||
let mut server = CharacterServerState::new(test_data.clone(), AuthToken("".into()));
|
||||
server.clients.insert(ClientId(1), fake_user.clone());
|
||||
let mut send = server.handle(ClientId(1), &RecvCharacterPacket::SetFlag(SetFlag {flags: 1})).await.unwrap().collect::<Vec<_>>();
|
||||
assert!(test_data.get_user_by_id(UserAccountId(3)).await.unwrap().flags == 1);
|
||||
|
@ -20,7 +20,8 @@ use crate::entity::account::{UserAccountEntity};
|
||||
pub const LOGIN_PORT: u16 = 12000;
|
||||
pub const COMMUNICATION_PORT: u16 = 12123;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error("")]
|
||||
pub enum LoginError {
|
||||
DbError
|
||||
}
|
||||
@ -62,11 +63,7 @@ pub async fn get_login_status(entity_gateway: &impl EntityGateway, pkt: &Login)
|
||||
let username = array_to_utf8(pkt.username).map_err(|_err| AccountStatus::Error)?;
|
||||
let password = array_to_utf8(pkt.password).map_err(|_err| AccountStatus::Error)?;
|
||||
let user = entity_gateway.get_user_by_name(username).await.map_err(|_| AccountStatus::InvalidUser)?;
|
||||
/*if user.is_currently_online() {
|
||||
return Err(AccountStatus::AlreadyOnline)
|
||||
}*/
|
||||
|
||||
log::info!("user: {}, activated {}", user.username, user.activated);
|
||||
if !user.activated {
|
||||
return Err(AccountStatus::PayUp)
|
||||
}
|
||||
@ -110,7 +107,7 @@ impl<EG: EntityGateway> LoginServerState<EG> {
|
||||
}
|
||||
}
|
||||
|
||||
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendLoginPacket>, LoginError> {
|
||||
async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendLoginPacket>, anyhow::Error> {
|
||||
match get_login_status(&self.entity_gateway, pkt).await.and_then(check_if_already_online) {
|
||||
Ok(mut user) => {
|
||||
user.at_login = true;
|
||||
@ -133,9 +130,10 @@ impl<EG: EntityGateway> LoginServerState<EG> {
|
||||
impl<EG: EntityGateway> ServerState for LoginServerState<EG> {
|
||||
type SendPacket = SendLoginPacket;
|
||||
type RecvPacket = RecvLoginPacket;
|
||||
type PacketError = LoginError;
|
||||
//type PacketError = LoginError;
|
||||
type PacketError = anyhow::Error;
|
||||
|
||||
async fn on_connect(&mut self, _id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, LoginError> {
|
||||
async fn on_connect(&mut self, _id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, anyhow::Error> {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let mut server_key = [0u8; 48];
|
||||
@ -150,7 +148,7 @@ impl<EG: EntityGateway> ServerState for LoginServerState<EG> {
|
||||
}
|
||||
|
||||
async fn handle(&mut self, id: ClientId, pkt: &Self::RecvPacket)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, Self::SendPacket)> + Send>, LoginError> {
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, Self::SendPacket)> + Send>, anyhow::Error> {
|
||||
Ok(match pkt {
|
||||
RecvLoginPacket::Login(login) => {
|
||||
Box::new(self.validate_login(id, login).await?
|
||||
@ -162,7 +160,7 @@ impl<EG: EntityGateway> ServerState for LoginServerState<EG> {
|
||||
})
|
||||
}
|
||||
|
||||
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendLoginPacket)>, LoginError> {
|
||||
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendLoginPacket)>, anyhow::Error> {
|
||||
if let Some(username) = self.clients.remove(&id) {
|
||||
if let Ok(mut user) = self.entity_gateway.get_user_by_name(username).await {
|
||||
user.at_login = false;
|
||||
@ -231,7 +229,7 @@ mod test {
|
||||
})
|
||||
}
|
||||
|
||||
async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> {
|
||||
async fn save_user(&mut self, _user: &UserAccountEntity) -> Result<(), GatewayError> {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
@ -1,112 +0,0 @@
|
||||
#![feature(const_generics)]
|
||||
|
||||
mod common;
|
||||
mod login;
|
||||
mod entity;
|
||||
|
||||
use std::thread;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bcrypt;
|
||||
|
||||
use libpso::character::settings;
|
||||
use libpso::character::character as pso_character;
|
||||
use libpso::character::guildcard;
|
||||
use libpso::{utf8_to_array, utf8_to_utf16_array};
|
||||
|
||||
use entity::gateway::EntityGateway;
|
||||
use entity::account::{UserAccount, UserSettings, GuildCardData};
|
||||
use entity::character::Character;
|
||||
|
||||
use login::login::LoginServerState;
|
||||
use login::character::CharacterServerState;
|
||||
|
||||
use std::time::SystemTime;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LoginStubData {
|
||||
users: HashMap<String, UserAccount>,
|
||||
characters: [Option<Character> ;4],
|
||||
}
|
||||
|
||||
impl LoginStubData {
|
||||
fn new() -> LoginStubData {
|
||||
let mut c = pso_character::Character::default();
|
||||
c.name = utf8_to_utf16_array!("Test Char", 16);
|
||||
|
||||
let mut users = HashMap::new();
|
||||
users.insert("hi".to_string(), UserAccount {
|
||||
id: 1,
|
||||
username: "hi".to_owned(),
|
||||
password: bcrypt::hash("qwer", 5).unwrap(),
|
||||
guildcard: None,
|
||||
team_id: None,
|
||||
banned: false,
|
||||
muted_until: SystemTime::now(),
|
||||
created_at: SystemTime::now(),
|
||||
flags: 0,
|
||||
});
|
||||
|
||||
LoginStubData {
|
||||
users: users,
|
||||
|
||||
characters: [Some(Character {
|
||||
id: 1,
|
||||
slot: 0,
|
||||
user_id: 1,
|
||||
character: c,
|
||||
}),
|
||||
None, None, None]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityGateway for LoginStubData {
|
||||
fn get_user_by_name(&self, username: String) -> Option<UserAccount> {
|
||||
self.users.get(&username).map(|user| user.clone())
|
||||
}
|
||||
|
||||
fn get_user_settings_by_user(&self, user: &UserAccount) -> Option<UserSettings> {
|
||||
Some(UserSettings {
|
||||
id: 0,
|
||||
user_id: user.id,
|
||||
settings: settings::UserSettings::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn set_user(&mut self, user: &UserAccount) {
|
||||
self.users.insert(user.username.clone(), user.clone());
|
||||
}
|
||||
|
||||
fn get_characters_by_user(&self, _user: &UserAccount) -> [Option<Character>; 4] {
|
||||
self.characters
|
||||
}
|
||||
|
||||
fn set_character(&mut self, char: &Character) {
|
||||
self.characters[char.slot as usize] = Some(char.clone());
|
||||
}
|
||||
|
||||
fn get_guild_card_data_by_user(&self, user: &UserAccount) -> GuildCardData {
|
||||
GuildCardData {
|
||||
id: 1,
|
||||
user_id: user.id,
|
||||
guildcard: guildcard::GuildCardData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("[login+character] starting server");
|
||||
|
||||
let auth_thread = thread::spawn(|| {
|
||||
let auth_state = LoginServerState::new(LoginStubData::new());
|
||||
common::mainloop::mainloop(auth_state, login::login::LOGIN_PORT);
|
||||
});
|
||||
let char_thread = thread::spawn(|| {
|
||||
let char_state = CharacterServerState::new(LoginStubData::new());
|
||||
common::mainloop::mainloop(char_state, login::character::CHARACTER_PORT);
|
||||
});
|
||||
|
||||
auth_thread.join().unwrap();
|
||||
char_thread.join().unwrap();
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#![feature(const_generics)]
|
||||
|
||||
mod common;
|
||||
mod patch;
|
||||
use crate::patch::patch::{PatchServerState, PatchTreeIterItem, generate_patch_tree, load_config, load_motd};
|
||||
|
||||
fn main() {
|
||||
println!("[patch] starting server");
|
||||
|
||||
let patch_config = load_config();
|
||||
let patch_motd: String = load_motd();
|
||||
|
||||
if let Err(_) = std::fs::read_dir(patch_config.path.as_str()) {
|
||||
println!("Patch directory {} does not exist. Attempting to create it...", patch_config.path.as_str());
|
||||
if let Err(err) = std::fs::create_dir(patch_config.path.as_str()) {
|
||||
panic!("Failed to create patch directory! \n{}", err);
|
||||
}
|
||||
}
|
||||
|
||||
let (patch_file_tree, patch_file_lookup) = generate_patch_tree(patch_config.path.as_str());
|
||||
println!("[patch] files to patch:");
|
||||
let mut indent = 0;
|
||||
for item in patch_file_tree.flatten() {
|
||||
match item {
|
||||
PatchTreeIterItem::Directory(path) => {
|
||||
let s = path.to_str().unwrap();
|
||||
println!("{: >2$}\u{2517}\u{2500}\u{2500} {}", "", s, indent * 4);
|
||||
indent += 1;
|
||||
},
|
||||
PatchTreeIterItem::File(path, id) => {
|
||||
let s = path.to_str().unwrap();
|
||||
println!("{: >3$}\u{2520}\u{2500}\u{2500} {} ({})", "", s, id, indent * 4);
|
||||
},
|
||||
PatchTreeIterItem::UpDirectory => {
|
||||
indent -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let patch_state = PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd);
|
||||
common::mainloop::mainloop(patch_state, patch_config.port);
|
||||
|
||||
println!("[patch] exiting...");
|
||||
}
|
@ -215,7 +215,7 @@ impl ItemManager {
|
||||
let inventory = self.character_inventory.get_mut(&character.id).unwrap();
|
||||
inventory.initialize_item_ids(base_inventory_id);
|
||||
let base_bank_id = ((area_client.local_client.id() as u32) << 21) | 0x20000;
|
||||
let default_bank = self.character_bank.get_mut(&character.id);//.unwrap().get_mut(&BankName("".to_string()));
|
||||
let default_bank = self.character_bank.get_mut(&character.id);
|
||||
match default_bank {
|
||||
Some(default_bank) => {
|
||||
default_bank.initialize_item_ids(base_bank_id);
|
||||
@ -933,16 +933,8 @@ impl ItemManager {
|
||||
character_id: character.id,
|
||||
slot: slot,
|
||||
equipped: true,
|
||||
}).await;
|
||||
entity_gateway.save_item(&ItemEntity{
|
||||
id: inventory_item.entity_id,
|
||||
location: ItemLocation::Inventory{
|
||||
character_id: character.id,
|
||||
slot: slot,
|
||||
equipped: true,
|
||||
},
|
||||
item: inventory_item.item.clone(),
|
||||
}).await;
|
||||
}).await?;
|
||||
entity_gateway.change_item(&inventory_item.entity_id, &inventory_item.item).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -968,15 +960,7 @@ impl ItemManager {
|
||||
slot: slot,
|
||||
equipped: false,
|
||||
}).await;
|
||||
entity_gateway.save_item(&ItemEntity{
|
||||
id: inventory_item.entity_id,
|
||||
location: ItemLocation::Inventory{
|
||||
character_id: character.id,
|
||||
slot: slot,
|
||||
equipped: false,
|
||||
},
|
||||
item: inventory_item.item.clone(),
|
||||
}).await;
|
||||
entity_gateway.change_item(&inventory_item.entity_id, &inventory_item.item).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ pub enum MonsterParseError {
|
||||
UnknownMonster(String),
|
||||
}
|
||||
|
||||
pub struct MonsterStatError;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq, enum_utils::FromStr, derive_more::Display)]
|
||||
pub enum MonsterType {
|
||||
@ -175,40 +176,37 @@ fn load_battle_param(filename: &str) -> HashMap<MonsterType, MonsterStats> {
|
||||
}).collect()
|
||||
}
|
||||
|
||||
pub fn load_monster_stats_table(mode: &RoomMode) -> HashMap<MonsterType, MonsterStats> {
|
||||
pub fn load_monster_stats_table(mode: &RoomMode) -> Result<HashMap<MonsterType, MonsterStats>, MonsterStatError> {
|
||||
match mode {
|
||||
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::Normal} => load_battle_param("ep1_multi_normal.toml"),
|
||||
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::Hard} => load_battle_param("ep1_multi_hard.toml"),
|
||||
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::VeryHard} => load_battle_param("ep1_multi_veryhard.toml"),
|
||||
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::Ultimate} => load_battle_param("ep1_multi_ultimate.toml"),
|
||||
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep1_multi_normal.toml")),
|
||||
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep1_multi_hard.toml")),
|
||||
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep1_multi_veryhard.toml")),
|
||||
RoomMode::Multi {episode: Episode::One, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep1_multi_ultimate.toml")),
|
||||
|
||||
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::Normal} => load_battle_param("ep2_multi_normal.toml"),
|
||||
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::Hard} => load_battle_param("ep2_multi_hard.toml"),
|
||||
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::VeryHard} => load_battle_param("ep2_multi_veryhard.toml"),
|
||||
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::Ultimate} => load_battle_param("ep2_multi_ultimate.toml"),
|
||||
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep2_multi_normal.toml")),
|
||||
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep2_multi_hard.toml")),
|
||||
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep2_multi_veryhard.toml")),
|
||||
RoomMode::Multi {episode: Episode::Two, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep2_multi_ultimate.toml")),
|
||||
|
||||
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::Normal} => load_battle_param("ep4_multi_normal.toml"),
|
||||
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::Hard} => load_battle_param("ep4_multi_hard.toml"),
|
||||
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::VeryHard} => load_battle_param("ep4_multi_veryhard.toml"),
|
||||
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::Ultimate} => load_battle_param("ep4_multi_ultimate.toml"),
|
||||
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep4_multi_normal.toml")),
|
||||
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep4_multi_hard.toml")),
|
||||
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep4_multi_veryhard.toml")),
|
||||
RoomMode::Multi {episode: Episode::Four, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep4_multi_ultimate.toml")),
|
||||
|
||||
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep1_solo_normal.toml")),
|
||||
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep1_solo_hard.toml")),
|
||||
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep1_solo_veryhard.toml")),
|
||||
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep1_solo_ultimate.toml")),
|
||||
|
||||
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep2_solo_normal.toml")),
|
||||
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep2_solo_hard.toml")),
|
||||
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep2_solo_veryhard.toml")),
|
||||
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep2_solo_ultimate.toml")),
|
||||
|
||||
|
||||
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::Normal} => load_battle_param("ep1_solo_normal.toml"),
|
||||
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::Hard} => load_battle_param("ep1_solo_hard.toml"),
|
||||
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::VeryHard} => load_battle_param("ep1_solo_veryhard.toml"),
|
||||
RoomMode::Single {episode: Episode::One, difficulty: Difficulty::Ultimate} => load_battle_param("ep1_solo_ultimate.toml"),
|
||||
|
||||
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::Normal} => load_battle_param("ep2_solo_normal.toml"),
|
||||
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::Hard} => load_battle_param("ep2_solo_hard.toml"),
|
||||
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::VeryHard} => load_battle_param("ep2_solo_veryhard.toml"),
|
||||
RoomMode::Single {episode: Episode::Two, difficulty: Difficulty::Ultimate} => load_battle_param("ep2_solo_ultimate.toml"),
|
||||
|
||||
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::Normal} => load_battle_param("ep4_solo_normal.toml"),
|
||||
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::Hard} => load_battle_param("ep4_solo_hard.toml"),
|
||||
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::VeryHard} => load_battle_param("ep4_solo_veryhard.toml"),
|
||||
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::Ultimate} => load_battle_param("ep4_solo_ultimate.toml"),
|
||||
_ => panic!(),
|
||||
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::Normal} => Ok(load_battle_param("ep4_solo_normal.toml")),
|
||||
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::Hard} => Ok(load_battle_param("ep4_solo_hard.toml")),
|
||||
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::VeryHard} => Ok(load_battle_param("ep4_solo_veryhard.toml")),
|
||||
RoomMode::Single {episode: Episode::Four, difficulty: Difficulty::Ultimate} => Ok(load_battle_param("ep4_solo_ultimate.toml")),
|
||||
_ => Err(MonsterStatError),
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,6 @@ where
|
||||
let client_and_drop = clients_in_area.into_iter()
|
||||
.filter_map(|area_client| {
|
||||
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
|
||||
warn!("drop is? {:?}", item_drop_type);
|
||||
(area_client, item_drop_type)
|
||||
})
|
||||
});
|
||||
@ -183,7 +182,6 @@ EG: EntityGateway
|
||||
let client_and_drop = clients_in_area.into_iter()
|
||||
.filter_map(|area_client| {
|
||||
room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
|
||||
warn!("drop is? {:?}", item_drop_type);
|
||||
(area_client, item_drop_type)
|
||||
})
|
||||
});
|
||||
|
@ -26,7 +26,7 @@ pub async fn request_exp<EG: EntityGateway>(id: ClientId,
|
||||
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
|
||||
let monster = room.maps.enemy_by_id(request_exp.enemy_id as usize)?;
|
||||
let monster_stats = room.monster_stats.get(&monster.monster).unwrap();
|
||||
let monster_stats = room.monster_stats.get(&monster.monster).ok_or(ShipError::UnknownMonster(monster.monster.clone()))?;
|
||||
|
||||
let exp_gain = if request_exp.last_hitter == 1 {
|
||||
monster_stats.exp
|
||||
|
@ -77,6 +77,9 @@ pub fn join_room(id: ClientId,
|
||||
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
item_manager.add_character_to_room(room_id, &client.character, area_client);
|
||||
|
||||
let leader = client_location.get_room_leader(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let join_room = builder::room::join_room(id, clients, client_location, room_id, &room)?;
|
||||
let add_to = builder::room::add_to_room(id, &client, &area_client, &leader, item_manager, level_table, room_id)?;
|
||||
@ -84,8 +87,6 @@ pub fn join_room(id: ClientId,
|
||||
let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap();
|
||||
room.bursting = true;
|
||||
|
||||
item_manager.add_character_to_room(room_id, &client.character, area_client);
|
||||
|
||||
let mut result: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = Box::new(
|
||||
vec![(id, SendShipPacket::JoinRoom(join_room))]
|
||||
.into_iter()
|
||||
|
@ -13,6 +13,7 @@ pub enum RoomCreationError {
|
||||
InvalidMode,
|
||||
InvalidEpisode(u8),
|
||||
InvalidDifficulty(u8),
|
||||
CouldNotLoadMonsterStats(RoomMode),
|
||||
|
||||
}
|
||||
|
||||
@ -231,7 +232,7 @@ impl RoomState {
|
||||
};
|
||||
|
||||
Ok(RoomState {
|
||||
monster_stats: Box::new(load_monster_stats_table(&room_mode)),
|
||||
monster_stats: Box::new(load_monster_stats_table(&room_mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(room_mode.clone()))?),
|
||||
mode: room_mode,
|
||||
random_seed: rand::thread_rng().gen(),
|
||||
name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(),
|
||||
|
105
src/ship/ship.rs
105
src/ship/ship.rs
@ -22,7 +22,7 @@ use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||
use crate::entity::account::{UserAccountEntity, UserSettingsEntity};
|
||||
use crate::entity::character::{CharacterEntity, SectionID};
|
||||
|
||||
use crate::ship::location::{ClientLocation, RoomLobby, MAX_ROOMS, ClientLocationError};
|
||||
use crate::ship::location::{ClientLocation, RoomLobby, MAX_ROOMS, ClientLocationError, GetNeighborError, GetClientsError, GetAreaError};
|
||||
|
||||
use crate::ship::items;
|
||||
use crate::ship::room;
|
||||
@ -45,6 +45,9 @@ pub enum ShipError {
|
||||
InvalidSlot(ClientId, u32),
|
||||
TooManyClients,
|
||||
ClientLocationError(#[from] ClientLocationError),
|
||||
GetNeighborError(#[from] GetNeighborError),
|
||||
GetClientsError(#[from] GetClientsError),
|
||||
GetAreaError(#[from] GetAreaError),
|
||||
MapsError(#[from] MapsError),
|
||||
MapAreaError(#[from] MapAreaError),
|
||||
InvalidRoom(u32),
|
||||
@ -63,6 +66,7 @@ pub enum ShipError {
|
||||
NotEnoughMeseta(ClientId, u32),
|
||||
ShopError,
|
||||
GatewayError(#[from] GatewayError),
|
||||
UnknownMonster(crate::ship::monster::MonsterType),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -294,6 +298,7 @@ pub struct ShipServerStateBuilder<EG: EntityGateway> {
|
||||
name: Option<String>,
|
||||
ip: Option<Ipv4Addr>,
|
||||
port: Option<u16>,
|
||||
auth_token: Option<AuthToken>,
|
||||
}
|
||||
|
||||
impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
|
||||
@ -303,6 +308,7 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
|
||||
name: None,
|
||||
ip: None,
|
||||
port: None,
|
||||
auth_token: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,6 +332,11 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn auth_token(mut self, auth_token: AuthToken) -> ShipServerStateBuilder<EG> {
|
||||
self.auth_token = Some(auth_token);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> ShipServerState<EG> {
|
||||
ShipServerState {
|
||||
entity_gateway: self.entity_gateway.unwrap(),
|
||||
@ -339,6 +350,7 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
|
||||
ip: self.ip.unwrap_or(Ipv4Addr::new(127,0,0,1)),
|
||||
port: self.port.unwrap_or(SHIP_PORT),
|
||||
shops: Box::new(ItemShops::new()),
|
||||
auth_token: self.auth_token.unwrap_or(AuthToken("".into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -355,6 +367,7 @@ pub struct ShipServerState<EG: EntityGateway> {
|
||||
ip: Ipv4Addr,
|
||||
port: u16,
|
||||
shops: Box<ItemShops>,
|
||||
auth_token: AuthToken,
|
||||
}
|
||||
|
||||
impl<EG: EntityGateway> ShipServerState<EG> {
|
||||
@ -362,93 +375,93 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
||||
ShipServerStateBuilder::new()
|
||||
}
|
||||
|
||||
async fn message(&mut self, id: ClientId, msg: &Message) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
match &msg.msg {
|
||||
async fn message(&mut self, id: ClientId, msg: &Message) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
Ok(match &msg.msg {
|
||||
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
|
||||
handler::message::request_exp(id, request_exp, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.rooms, &self.level_table).await?
|
||||
},
|
||||
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
|
||||
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?
|
||||
},
|
||||
GameMessage::DropCoordinates(drop_coordinates) => {
|
||||
handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms)
|
||||
handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms)?
|
||||
},
|
||||
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
|
||||
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?
|
||||
},
|
||||
GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) |
|
||||
GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) |
|
||||
GameMessage::PlayerLoadedIn(_) | GameMessage::PlayerWalking(_) | GameMessage::PlayerRunning(_) |
|
||||
GameMessage::PlayerWarped(_) | GameMessage::PlayerChangedFloor(_) | GameMessage::InitializeSpeechNpc(_) => {
|
||||
handler::message::update_player_position(id, &msg, &mut self.clients, &mut self.client_location, &self.rooms)
|
||||
handler::message::update_player_position(id, &msg, &mut self.clients, &mut self.client_location, &self.rooms)?
|
||||
},
|
||||
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) => {
|
||||
handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await
|
||||
handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await?
|
||||
},
|
||||
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) => {
|
||||
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
|
||||
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?
|
||||
},
|
||||
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?
|
||||
},
|
||||
GameMessage::PlayerUnequipItem(player_unequip_item) => {
|
||||
handler::message::player_unequips_item(id, &player_unequip_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await
|
||||
handler::message::player_unequips_item(id, &player_unequip_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
|
||||
},
|
||||
GameMessage::SortItems(sort_items) => {
|
||||
handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await
|
||||
handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
|
||||
},
|
||||
_ => {
|
||||
let cmsg = msg.clone();
|
||||
Ok(Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||
Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(cmsg.clone()))
|
||||
})))
|
||||
}))
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn direct_message(&mut self, id: ClientId, msg: &DirectMessage) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
async fn direct_message(&mut self, id: ClientId, msg: &DirectMessage) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
let target = msg.flag;
|
||||
match &msg.msg {
|
||||
Ok(match &msg.msg {
|
||||
GameMessage::GuildcardSend(guildcard_send) => {
|
||||
Ok(handler::direct_message::guildcard_send(id, guildcard_send, target, &self.client_location, &self.clients))
|
||||
handler::direct_message::guildcard_send(id, guildcard_send, target, &self.client_location, &self.clients)
|
||||
},
|
||||
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
|
||||
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?
|
||||
},
|
||||
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
|
||||
handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await?
|
||||
},
|
||||
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
|
||||
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?
|
||||
},
|
||||
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) => {
|
||||
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.item_manager).await
|
||||
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.item_manager).await?
|
||||
},
|
||||
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
|
||||
handler::direct_message::shop_request(id, shop_request, &self.client_location, &mut self.clients, &self.rooms, &self.level_table, &mut self.shops).await?
|
||||
},
|
||||
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
|
||||
handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.item_manager).await?
|
||||
},
|
||||
_ => {
|
||||
let cmsg = msg.clone();
|
||||
Ok(Box::new(self.client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
Box::new(self.client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(cmsg.clone()))
|
||||
})))
|
||||
}))
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,9 +469,9 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
||||
impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
||||
type SendPacket = SendShipPacket;
|
||||
type RecvPacket = RecvShipPacket;
|
||||
type PacketError = ShipError;
|
||||
type PacketError = anyhow::Error;
|
||||
|
||||
async fn on_connect(&mut self, _id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, ShipError> {
|
||||
async fn on_connect(&mut self, _id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, anyhow::Error> {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let mut server_key = [0u8; 48];
|
||||
@ -473,7 +486,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
||||
}
|
||||
|
||||
async fn handle(&mut self, id: ClientId, pkt: &RecvShipPacket)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + 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)))
|
||||
@ -580,13 +593,13 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
||||
})
|
||||
}
|
||||
|
||||
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
// TODO: don't unwrap!
|
||||
let client = self.clients.get(&id).unwrap();
|
||||
let area_client = self.client_location.get_local_client(id).unwrap();
|
||||
let neighbors = self.client_location.get_client_neighbors(id).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)?;
|
||||
|
||||
let pkt = match self.client_location.get_area(id).unwrap() {
|
||||
let pkt = match self.client_location.get_area(id)? {
|
||||
RoomLobby::Room(room) => {
|
||||
if neighbors.len() == 0 {
|
||||
self.rooms[room.0] = None;
|
||||
@ -622,12 +635,14 @@ impl<EG: EntityGateway> InterserverActor for ShipServerState<EG> {
|
||||
type Error = ();
|
||||
|
||||
async fn on_connect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
|
||||
vec![ /* ShipMessage::Authenticate(AuthToken("hi".into())), */ (id, ShipMessage::NewShip(Ship {
|
||||
name: self.name.clone(),
|
||||
ip: self.ip.clone(),
|
||||
port: self.port,
|
||||
block_count: 2,
|
||||
})) ]
|
||||
vec![
|
||||
(id, ShipMessage::Authenticate(self.auth_token.clone())),
|
||||
(id, ShipMessage::NewShip(Ship {
|
||||
name: self.name.clone(),
|
||||
ip: self.ip.clone(),
|
||||
port: self.port,
|
||||
block_count: 2,
|
||||
})) ]
|
||||
}
|
||||
|
||||
async fn action(&mut self, _id: ServerId, _msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
|
||||
|
@ -59,6 +59,13 @@ pub async fn create_room<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id:
|
||||
create_room_with_difficulty(ship, id, name, password, Difficulty::Normal).await;
|
||||
}
|
||||
|
||||
pub async fn leave_room<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId) {
|
||||
ship.handle(id, &RecvShipPacket::LobbySelect(LobbySelect {
|
||||
menu: 3,
|
||||
lobby: 0,
|
||||
})).await.unwrap().for_each(drop);
|
||||
}
|
||||
|
||||
pub async fn create_room_with_difficulty<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, name: &str, password: &str, difficulty: Difficulty) {
|
||||
ship.handle(id, &RecvShipPacket::CreateRoom(CreateRoom {
|
||||
unknown: [0; 2],
|
||||
@ -79,4 +86,5 @@ pub async fn join_room<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: Cl
|
||||
menu: ROOM_MENU_ID,
|
||||
item: room_id,
|
||||
})).await.unwrap().for_each(drop);
|
||||
ship.handle(id, &RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap().for_each(drop);
|
||||
}
|
||||
|
104
tests/test_rooms.rs
Normal file
104
tests/test_rooms.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use elseware::common::serverstate::{ClientId, ServerState};
|
||||
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
||||
use elseware::entity::item;
|
||||
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
|
||||
|
||||
use libpso::packet::ship::*;
|
||||
use libpso::packet::messages::*;
|
||||
|
||||
#[path = "common.rs"]
|
||||
mod common;
|
||||
use common::*;
|
||||
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_item_ids_reset_when_rejoining_rooms() {
|
||||
let mut entity_gateway = InMemoryGateway::new();
|
||||
|
||||
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
|
||||
|
||||
for slot in 0..3 {
|
||||
entity_gateway.create_item(
|
||||
item::NewItemEntity {
|
||||
item: item::ItemDetail::Weapon(
|
||||
item::weapon::Weapon {
|
||||
weapon: item::weapon::WeaponType::Saber,
|
||||
grind: 0,
|
||||
special: None,
|
||||
attrs: [None, None, None],
|
||||
tekked: true,
|
||||
modifiers: Vec::new(),
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
slot: slot,
|
||||
equipped: false,
|
||||
}
|
||||
}).await.unwrap();
|
||||
}
|
||||
|
||||
for slot in 0..10 {
|
||||
entity_gateway.create_item(
|
||||
item::NewItemEntity {
|
||||
item: item::ItemDetail::Weapon(
|
||||
item::weapon::Weapon {
|
||||
weapon: item::weapon::WeaponType::Saber,
|
||||
grind: 0,
|
||||
special: None,
|
||||
attrs: [None, None, None],
|
||||
tekked: true,
|
||||
modifiers: Vec::new(),
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char2.id,
|
||||
slot: slot,
|
||||
equipped: false,
|
||||
}
|
||||
}).await.unwrap();
|
||||
}
|
||||
|
||||
let mut ship = ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
.build();
|
||||
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
|
||||
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
join_lobby(&mut ship, ClientId(2)).await;
|
||||
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
let p = ship.handle(ClientId(2), &RecvShipPacket::MenuSelect(MenuSelect {
|
||||
menu: ROOM_MENU_ID,
|
||||
item: 0,
|
||||
})).await.unwrap().collect::<Vec<_>>();
|
||||
ship.handle(ClientId(2), &RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap().for_each(drop);
|
||||
|
||||
match &p[1].1 {
|
||||
SendShipPacket::AddToRoom(add_to) => {
|
||||
println!("addto {:?}", add_to);
|
||||
assert_eq!(add_to.playerinfo.inventory.items.iter().map(|k| k.item_id).collect::<Vec<_>>(),
|
||||
vec![0x210000,0x210001,0x210002,0x210003,0x210004,0x210005,0x210006,0x210007,0x210008,0x210009,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
|
||||
},
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
leave_room(&mut ship, ClientId(2)).await;
|
||||
|
||||
let p = ship.handle(ClientId(2), &RecvShipPacket::MenuSelect(MenuSelect {
|
||||
menu: ROOM_MENU_ID,
|
||||
item: 0,
|
||||
})).await.unwrap().collect::<Vec<_>>();
|
||||
|
||||
match &p[1].1 {
|
||||
SendShipPacket::AddToRoom(add_to) => {
|
||||
assert_eq!(add_to.playerinfo.inventory.items.iter().map(|k| k.item_id).collect::<Vec<_>>(),
|
||||
vec![0x210000,0x210001,0x210002,0x210003,0x210004,0x210005,0x210006,0x210007,0x210008,0x210009,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
|
||||
},
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user