fix the whole single mutex on the server state thing #121
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -1061,7 +1061,6 @@ checksum = "739e9d7726dc32173fed2d69d17eef3c54682169e4e20ff1d0a45dcd37063cef"
|
||||
[[package]]
|
||||
name = "libpso"
|
||||
version = "0.1.0"
|
||||
source = "git+http://git.sharnoth.com/jake/libpso#4fba0529aef169c67a99709582109ca5c68f15de"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"psopacket",
|
||||
@ -1420,7 +1419,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "psopacket"
|
||||
version = "1.0.0"
|
||||
source = "git+http://git.sharnoth.com/jake/libpso#4fba0529aef169c67a99709582109ca5c68f15de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2071,18 +2069,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "elseware"
|
||||
version = "0.1.0"
|
||||
authors = ["Jake Probst <jake.probst@gmail.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libpso = { git = "http://git.sharnoth.com/jake/libpso" }
|
||||
@ -22,7 +22,7 @@ fern = { version = "0.5", features = ["colored"] }
|
||||
byteorder = "1"
|
||||
enum-utils = "0.1.2"
|
||||
derive_more = { version = "0.99.3", features = ["display"]}
|
||||
thiserror = "1.0.15"
|
||||
thiserror = "1.0.37"
|
||||
ages-prs = "0.1"
|
||||
async-trait = "0.1.51"
|
||||
async-recursion= "1.0.0"
|
||||
|
@ -2,7 +2,6 @@ use log::{info};
|
||||
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() {
|
||||
@ -37,15 +36,23 @@ fn main() {
|
||||
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 login_state = LoginServerState::new(entity_gateway.clone(), charserv_ip);
|
||||
let login_loop = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_server(login_state, elseware::login::login::LOGIN_PORT).await;
|
||||
});
|
||||
|
||||
let char_state = CharacterServerState::new(entity_gateway, AuthToken(shipgate_token));
|
||||
let character_loop = character_mainloop(char_state, elseware::login::character::CHARACTER_PORT, elseware::login::login::COMMUNICATION_PORT);
|
||||
let sub_char_state = char_state.clone();
|
||||
let character_loop = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_server(sub_char_state, elseware::login::character::CHARACTER_PORT).await;
|
||||
});
|
||||
|
||||
let inter_character_loop = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_interserver_listen(char_state, elseware::login::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
info!("[auth/character] starting server");
|
||||
async_std::task::block_on(async move {
|
||||
futures::future::join_all(vec![login_loop, character_loop]).await
|
||||
futures::future::join_all(vec![login_loop, character_loop, inter_character_loop]).await
|
||||
});
|
||||
}
|
||||
|
@ -1,19 +1,18 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use log::{info};
|
||||
|
||||
use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config, load_motd};
|
||||
use elseware::common::interserver::AuthToken;
|
||||
use elseware::login::login::LoginServerState;
|
||||
use elseware::login::character::CharacterServerState;
|
||||
use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config, load_motd};
|
||||
use elseware::ship::ship::ShipServerStateBuilder;
|
||||
use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use elseware::entity::gateway::{EntityGateway, InMemoryGateway, PostgresGateway};
|
||||
use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
|
||||
use elseware::entity::character::NewCharacterEntity;
|
||||
use elseware::entity::item::{NewItemEntity, ItemDetail, InventoryItemEntity};
|
||||
use elseware::common::interserver::AuthToken;
|
||||
|
||||
use elseware::entity::item;
|
||||
use elseware::common::mainloop::*;
|
||||
|
||||
fn setup_logger() {
|
||||
let colors = fern::colors::ColoredLevelConfig::new()
|
||||
@ -325,7 +324,10 @@ fn main() {
|
||||
};
|
||||
entity_gateway.set_character_equips(&character.id, &equipped).await.unwrap();
|
||||
|
||||
let inventory = item::InventoryEntity::new(vec![InventoryItemEntity::from(item0), item1.into(), item2_w.into(), item3.into(), item4.into(), item5_m.into(), item6.into(), item6_1.into(), item7_a.into(), item8_s.into(), item9_u0.into(), item10_u1.into(), item11_u2.into(), item12_u3.into(), item13.into(), item14.into(), monomates.into()]);
|
||||
let inventory = item::InventoryEntity::new(vec![InventoryItemEntity::from(item0), item1.into(), item2_w.into(), item3.into(),
|
||||
item4.into(), item5_m.into(), item6.into(), item6_1.into(), item7_a.into(),
|
||||
item8_s.into(), item9_u0.into(), item10_u1.into(), item11_u2.into(), item12_u3.into(),
|
||||
item13.into(), item14.into(), monomates.into()]);
|
||||
entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap();
|
||||
entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), &item::BankName("".into())).await.unwrap();
|
||||
}
|
||||
@ -334,47 +336,77 @@ fn main() {
|
||||
let patch_config = load_config();
|
||||
let patch_motd = load_motd();
|
||||
let (patch_file_tree, patch_file_lookup) = generate_patch_tree(patch_config.path.as_str());
|
||||
let patch_state = Box::new(PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd));
|
||||
let patch_loop = patch_mainloop(*patch_state, patch_config.port);
|
||||
let patch_state = PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd);
|
||||
let patch_loop = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_server(patch_state, patch_config.port).await;
|
||||
});
|
||||
|
||||
let thread_entity_gateway = entity_gateway.clone();
|
||||
info!("[auth] starting server");
|
||||
let login_state = Box::new(LoginServerState::new(thread_entity_gateway, "127.0.0.1".parse().unwrap()));
|
||||
let login_loop = login_mainloop(*login_state, elseware::login::login::LOGIN_PORT);
|
||||
let login_state = LoginServerState::new(entity_gateway.clone(), "127.0.0.1".parse().unwrap());
|
||||
let login_loop = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_server(login_state, elseware::login::login::LOGIN_PORT).await;
|
||||
});
|
||||
|
||||
let thread_entity_gateway = entity_gateway.clone();
|
||||
info!("[character] starting server");
|
||||
let char_state = Box::new(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 char_state = CharacterServerState::new(entity_gateway.clone(), AuthToken("".into()));
|
||||
let sub_char_state = char_state.clone();
|
||||
let character_loop = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_server(sub_char_state, elseware::login::character::CHARACTER_PORT).await;
|
||||
});
|
||||
|
||||
let thread_entity_gateway = entity_gateway.clone();
|
||||
info!("[ship] starting server");
|
||||
let ship_state = Box::new(ShipServerStateBuilder::default()
|
||||
let sub_char_state = char_state.clone();
|
||||
let inter_character_loop = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_interserver_listen(sub_char_state, elseware::login::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
info!("[ship] starting servers");
|
||||
let ship_state = ShipServerStateBuilder::default()
|
||||
.name("US/Sona-Nyl".into())
|
||||
.ip(Ipv4Addr::new(127,0,0,1))
|
||||
.port(elseware::ship::ship::SHIP_PORT)
|
||||
.gateway(thread_entity_gateway)
|
||||
.build());
|
||||
let ship_loop = ship_mainloop(*ship_state, elseware::ship::ship::SHIP_PORT, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT);
|
||||
.gateway(entity_gateway.clone())
|
||||
.build();
|
||||
let sub_ship_state = ship_state.clone();
|
||||
let ship_loop1 = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT).await;
|
||||
});
|
||||
let sub_ship_state = ship_state.clone();
|
||||
let inter_ship_loop1 = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
let thread_entity_gateway = entity_gateway.clone();
|
||||
let ship_state = Box::new(ShipServerStateBuilder::default()
|
||||
let ship_state = ShipServerStateBuilder::default()
|
||||
.name("EU/Dylath-Leen".into())
|
||||
.ip(Ipv4Addr::new(127,0,0,1))
|
||||
.port(elseware::ship::ship::SHIP_PORT+2000)
|
||||
.gateway(thread_entity_gateway)
|
||||
.build());
|
||||
let ship_loop2 = ship_mainloop(*ship_state, elseware::ship::ship::SHIP_PORT+2000, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT);
|
||||
.port(elseware::ship::ship::SHIP_PORT+200)
|
||||
.gateway(entity_gateway.clone())
|
||||
.build();
|
||||
let sub_ship_state = ship_state.clone();
|
||||
let ship_loop2 = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT+2000).await;
|
||||
});
|
||||
let sub_ship_state = ship_state.clone();
|
||||
let inter_ship_loop2 = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
let thread_entity_gateway = entity_gateway.clone();
|
||||
let ship_state = Box::new(ShipServerStateBuilder::default()
|
||||
let ship_state = ShipServerStateBuilder::default()
|
||||
.name("JP/Thalarion".into())
|
||||
.ip(Ipv4Addr::new(127,0,0,1))
|
||||
.port(elseware::ship::ship::SHIP_PORT+3000)
|
||||
.gateway(thread_entity_gateway)
|
||||
.build());
|
||||
let ship_loop3 = ship_mainloop(*ship_state, elseware::ship::ship::SHIP_PORT+3000, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT);
|
||||
.gateway(entity_gateway.clone())
|
||||
.build();
|
||||
let sub_ship_state = ship_state.clone();
|
||||
let ship_loop3 = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT+3000).await;
|
||||
});
|
||||
let sub_ship_state = ship_state.clone();
|
||||
let inter_ship_loop3 = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_interserver_connect(sub_ship_state, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
futures::future::join_all(vec![patch_loop, login_loop, character_loop, ship_loop, ship_loop2, ship_loop3]).await;
|
||||
futures::future::join_all(vec![patch_loop, login_loop, character_loop, inter_character_loop,
|
||||
ship_loop1, ship_loop2, ship_loop3,
|
||||
inter_ship_loop1, inter_ship_loop2, inter_ship_loop3]).await;
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config_env, load_motd};
|
||||
use log::{info};
|
||||
use elseware::common::mainloop::patch_mainloop;
|
||||
|
||||
fn main() {
|
||||
info!("[patch] starting server");
|
||||
@ -8,7 +7,10 @@ fn main() {
|
||||
let patch_motd = load_motd();
|
||||
let (patch_file_tree, patch_file_lookup) = generate_patch_tree(patch_config.path.as_str());
|
||||
let patch_state = PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd);
|
||||
let patch_loop = patch_mainloop(patch_state, patch_config.port);
|
||||
|
||||
let patch_loop = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_server(patch_state, patch_config.port).await;
|
||||
});
|
||||
|
||||
async_std::task::block_on(async move {
|
||||
patch_loop.await
|
||||
|
@ -1,7 +1,6 @@
|
||||
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() {
|
||||
@ -37,6 +36,7 @@ fn main() {
|
||||
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::default()
|
||||
.name(ship_name)
|
||||
.ip(ip)
|
||||
@ -46,10 +46,17 @@ fn main() {
|
||||
.build();
|
||||
|
||||
let shipgate_ip = std::env::var("SHIPGATE_IP").unwrap().parse().unwrap();
|
||||
let ship_loop = ship_mainloop(ship_state, elseware::ship::ship::SHIP_PORT, shipgate_ip, elseware::login::login::COMMUNICATION_PORT);
|
||||
|
||||
|
||||
let sub_ship_state = ship_state.clone();
|
||||
let ship_loop = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_server(sub_ship_state, elseware::ship::ship::SHIP_PORT).await;
|
||||
});
|
||||
let inter_ship_loop = async_std::task::spawn(async move {
|
||||
elseware::common::mainloop::run_interserver_connect(ship_state, shipgate_ip, elseware::login::login::COMMUNICATION_PORT).await;
|
||||
});
|
||||
|
||||
info!("[auth/character] starting server");
|
||||
async_std::task::block_on(async move {
|
||||
ship_loop.await
|
||||
futures::future::join_all(vec![ship_loop, inter_ship_loop]).await;
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use async_std::channel;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::de::DeserializeOwned;
|
||||
use crate::entity::account::UserAccountId;
|
||||
@ -46,12 +47,13 @@ pub enum ShipMessage {
|
||||
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait InterserverActor {
|
||||
pub trait InterserverActor: Clone {
|
||||
type SendMessage: Serialize;
|
||||
type RecvMessage: DeserializeOwned;
|
||||
type Error;
|
||||
|
||||
async fn on_connect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)>;
|
||||
async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error>;
|
||||
async fn on_action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error>;
|
||||
async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)>;
|
||||
async fn set_sender(&mut self, server_id: ServerId, tx: channel::Sender<Self::SendMessage>);
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
use std::pin::Pin;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use async_std::channel;
|
||||
use async_std::io::prelude::{ReadExt, WriteExt};
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
use futures::future::Future;
|
||||
use log::{trace, info, warn};
|
||||
use async_std::sync::{Arc, Mutex};
|
||||
use async_std::io::prelude::{ReadExt, WriteExt};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use libpso::crypto::{PSOCipher, NullCipher, CipherError};
|
||||
use libpso::PacketParseError;
|
||||
@ -39,15 +40,15 @@ impl From<PacketParseError> for NetworkError {
|
||||
}
|
||||
}
|
||||
|
||||
struct PacketReceiver {
|
||||
socket: Arc<async_std::net::TcpStream>,
|
||||
cipher: Arc<Mutex<Box<dyn PSOCipher + Send>>>,
|
||||
pub struct PacketReceiver<C: PSOCipher> {
|
||||
socket: async_std::net::TcpStream,
|
||||
cipher: C,
|
||||
recv_buffer: Vec<u8>,
|
||||
incoming_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl PacketReceiver {
|
||||
fn new(socket: Arc<async_std::net::TcpStream>, cipher: Arc<Mutex<Box<dyn PSOCipher + Send>>>) -> PacketReceiver {
|
||||
impl<C: PSOCipher> PacketReceiver<C> {
|
||||
pub fn new(socket: async_std::net::TcpStream, cipher: C) -> PacketReceiver<C> {
|
||||
PacketReceiver {
|
||||
socket,
|
||||
cipher,
|
||||
@ -59,7 +60,7 @@ impl PacketReceiver {
|
||||
async fn fill_recv_buffer(&mut self) -> Result<(), NetworkError> {
|
||||
let mut data = [0u8; 0x8000];
|
||||
|
||||
let mut socket = &*self.socket;
|
||||
let mut socket = self.socket.clone();
|
||||
let len = socket.read(&mut data).await?;
|
||||
if len == 0 {
|
||||
return Err(NetworkError::ClientDisconnected);
|
||||
@ -68,17 +69,17 @@ impl PacketReceiver {
|
||||
self.recv_buffer.extend_from_slice(&data[..len]);
|
||||
|
||||
let mut dec_buf = {
|
||||
let mut cipher = self.cipher.lock().await;
|
||||
let block_chunk_len = self.recv_buffer.len() / cipher.block_size() * cipher.block_size();
|
||||
//let mut cipher = self.cipher.lock().await;
|
||||
let block_chunk_len = self.recv_buffer.len() / self.cipher.block_size() * self.cipher.block_size();
|
||||
let buf = self.recv_buffer.drain(..block_chunk_len).collect();
|
||||
cipher.decrypt(&buf)?
|
||||
self.cipher.decrypt(&buf)?
|
||||
};
|
||||
self.incoming_data.append(&mut dec_buf);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn recv_pkts<R: RecvServerPacket + Send + std::fmt::Debug>(&mut self) -> Result<Vec<R>, NetworkError> {
|
||||
pub async fn recv_pkts<R: RecvServerPacket + std::fmt::Debug>(&mut self) -> Result<Vec<R>, NetworkError> {
|
||||
self.fill_recv_buffer().await?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
@ -88,7 +89,7 @@ impl PacketReceiver {
|
||||
}
|
||||
let pkt_size = u16::from_le_bytes([self.incoming_data[0], self.incoming_data[1]]) as usize;
|
||||
let mut pkt_len = pkt_size;
|
||||
while pkt_len % self.cipher.lock().await.block_size() != 0 {
|
||||
while pkt_len % self.cipher.block_size() != 0 {
|
||||
pkt_len += 1;
|
||||
}
|
||||
|
||||
@ -114,204 +115,159 @@ impl PacketReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_pkt<S: SendServerPacket + Send + std::fmt::Debug>(socket: Arc<async_std::net::TcpStream>,
|
||||
cipher: Arc<Mutex<Box<dyn PSOCipher + Send>>>, pkt: S)
|
||||
-> Result<(), NetworkError>
|
||||
async fn recv_loop<STATE, S, R, C, E>(mut state: STATE,
|
||||
socket: async_std::net::TcpStream,
|
||||
client_id: ClientId,
|
||||
cipher: C,
|
||||
clients: Arc<RwLock<HashMap<ClientId, channel::Sender<S>>>>)
|
||||
where
|
||||
STATE: ServerState<SendPacket=S, RecvPacket=R, Cipher=C, PacketError=E> + Send,
|
||||
S: SendServerPacket + Debug + Send,
|
||||
R: RecvServerPacket + Debug + Send,
|
||||
C: PSOCipher + Send,
|
||||
E: std::fmt::Debug + Send,
|
||||
{
|
||||
let mut pkt_receiver = PacketReceiver::new(socket, cipher);
|
||||
loop {
|
||||
match pkt_receiver.recv_pkts::<R>().await {
|
||||
Ok(pkts) => {
|
||||
for pkt in pkts {
|
||||
info!("[recv from {:?}] {:#?}", client_id, pkt);
|
||||
match state.handle(client_id, pkt).await {
|
||||
Ok(response) => {
|
||||
for resp in response {
|
||||
clients
|
||||
.read()
|
||||
.await
|
||||
.get(&resp.0)
|
||||
.unwrap()
|
||||
.send(resp.1)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("[client recv {:?}] error {:?} ", client_id, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
match err {
|
||||
NetworkError::ClientDisconnected => {
|
||||
info!("[client recv {:?}] disconnected", client_id);
|
||||
for pkt in state.on_disconnect(client_id).await.unwrap() {
|
||||
clients
|
||||
.read()
|
||||
.await
|
||||
.get(&pkt.0)
|
||||
.unwrap()
|
||||
.send(pkt.1)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
clients
|
||||
.write()
|
||||
.await
|
||||
.remove(&client_id);
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
warn!("[client {:?} recv error] {:?}", client_id, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async fn send_pkt<S, C>(socket: &mut async_std::net::TcpStream,
|
||||
cipher: &mut C,
|
||||
pkt: &S)
|
||||
-> Result<(), NetworkError>
|
||||
where
|
||||
S: SendServerPacket + std::fmt::Debug,
|
||||
C: PSOCipher,
|
||||
{
|
||||
let buf = pkt.as_bytes();
|
||||
trace!("[send buf] {:?}", buf);
|
||||
let cbuf = cipher.lock().await.encrypt(&buf)?;
|
||||
let mut ssock = &*socket;
|
||||
ssock.write_all(&cbuf).await?;
|
||||
let cbuf = cipher.encrypt(&buf)?;
|
||||
socket.write_all(&cbuf).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
enum ClientAction<S, R> {
|
||||
NewClient(ClientId, async_std::channel::Sender<S>),
|
||||
Packet(ClientId, R),
|
||||
Disconnect(ClientId),
|
||||
}
|
||||
|
||||
enum ServerStateAction<S> {
|
||||
Cipher(Box<dyn PSOCipher + Send + Sync>, Box<dyn PSOCipher + Send + Sync>),
|
||||
Packet(S),
|
||||
Disconnect,
|
||||
}
|
||||
|
||||
fn client_recv_loop<S, R>(client_id: ClientId,
|
||||
socket: Arc<async_std::net::TcpStream>,
|
||||
cipher: Arc<Mutex<Box<dyn PSOCipher + Send>>>,
|
||||
server_sender: async_std::channel::Sender<ClientAction<ServerStateAction<S>, R>>,
|
||||
client_sender: async_std::channel::Sender<ServerStateAction<S>>)
|
||||
async fn send_loop<S, C>(mut socket: async_std::net::TcpStream, client_id: ClientId, mut cipher: C, packet_queue: channel::Receiver<S>)
|
||||
where
|
||||
S: SendServerPacket + std::fmt::Debug + Send + 'static,
|
||||
R: RecvServerPacket + std::fmt::Debug + Send + 'static,
|
||||
S: SendServerPacket + std::fmt::Debug,
|
||||
C: PSOCipher,
|
||||
{
|
||||
async_std::task::spawn(async move {
|
||||
server_sender.send(ClientAction::NewClient(client_id, client_sender)).await.unwrap();
|
||||
let mut pkt_receiver = PacketReceiver::new(socket, cipher);
|
||||
loop {
|
||||
match packet_queue.recv().await {
|
||||
Ok(pkt) => {
|
||||
if let Err(err) = send_pkt(&mut socket, &mut cipher, &pkt).await {
|
||||
warn!("error sending pkt {:#?} to {:?} {:?}", pkt, client_id, err);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
info!("send to {:?} failed: {:?}", client_id, err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
match pkt_receiver.recv_pkts().await {
|
||||
Ok(pkts) => {
|
||||
for pkt in pkts {
|
||||
info!("[recv from {:?}] {:?}", client_id, pkt);
|
||||
server_sender.send(ClientAction::Packet(client_id, pkt)).await.unwrap();
|
||||
}
|
||||
pub async fn run_server<STATE, S, R, C, E>(mut state: STATE, port: u16)
|
||||
where
|
||||
STATE: ServerState<SendPacket=S, RecvPacket=R, Cipher=C, PacketError=E> + Send + 'static,
|
||||
S: SendServerPacket + std::fmt::Debug + Send + 'static,
|
||||
R: RecvServerPacket + std::fmt::Debug + Send,
|
||||
C: PSOCipher + Send + 'static,
|
||||
E: std::fmt::Debug + Send,
|
||||
{
|
||||
let listener = async_std::net::TcpListener::bind(&std::net::SocketAddr::from((std::net::Ipv4Addr::new(0,0,0,0), port))).await.unwrap();
|
||||
let mut id = 0;
|
||||
|
||||
let clients = Arc::new(RwLock::new(HashMap::new()));
|
||||
|
||||
loop {
|
||||
let (mut socket, addr) = listener.accept().await.unwrap();
|
||||
id += 1;
|
||||
|
||||
let client_id = crate::common::serverstate::ClientId(id);
|
||||
info!("new client {:?} {:?} {:?}", client_id, socket, addr);
|
||||
|
||||
let (client_tx, client_rx) = async_std::channel::unbounded();
|
||||
|
||||
clients
|
||||
.write()
|
||||
.await
|
||||
.insert(client_id, client_tx.clone());
|
||||
|
||||
let mut cipher_in: Option<C> = None;
|
||||
let mut cipher_out: Option<C> = None;
|
||||
|
||||
for action in state.on_connect(client_id).await.unwrap() {
|
||||
match action {
|
||||
OnConnect::Cipher(cin, cout) => {
|
||||
cipher_in = Some(cin);
|
||||
cipher_out = Some(cout);
|
||||
},
|
||||
Err(err) => {
|
||||
match err {
|
||||
NetworkError::ClientDisconnected => {
|
||||
trace!("[client disconnected] {:?}", client_id);
|
||||
server_sender.send(ClientAction::Disconnect(client_id)).await.unwrap();
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
warn!("[client {:?} recv error] {:?}", client_id, err);
|
||||
}
|
||||
}
|
||||
OnConnect::Packet(pkt) => {
|
||||
send_pkt(&mut socket, &mut NullCipher {}, &pkt).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let rstate = state.clone();
|
||||
let rsocket = socket.clone();
|
||||
let rclients = clients.clone();
|
||||
async_std::task::spawn(async move {
|
||||
recv_loop(rstate, rsocket, client_id, cipher_in.unwrap(), rclients).await
|
||||
});
|
||||
|
||||
async_std::task::spawn(async move {
|
||||
send_loop(socket, client_id, cipher_out.unwrap(), client_rx).await
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn client_send_loop<S>(client_id: ClientId,
|
||||
socket: Arc<async_std::net::TcpStream>,
|
||||
cipher_in: Arc<Mutex<Box<dyn PSOCipher + Send>>>,
|
||||
cipher_out: Arc<Mutex<Box<dyn PSOCipher + Send>>>,
|
||||
client_receiver: async_std::channel::Receiver<ServerStateAction<S>>)
|
||||
where
|
||||
S: SendServerPacket + std::fmt::Debug + Send + 'static,
|
||||
{
|
||||
async_std::task::spawn(async move {
|
||||
loop {
|
||||
let action = client_receiver.recv().await.unwrap();
|
||||
match action {
|
||||
ServerStateAction::Cipher(inc, outc) => {
|
||||
*cipher_in.lock().await = inc;
|
||||
*cipher_out.lock().await = outc;
|
||||
}
|
||||
ServerStateAction::Packet(pkt) => {
|
||||
info!("[send to {:?}] {:?}", client_id, pkt);
|
||||
if let Err(err) = send_pkt(socket.clone(), cipher_out.clone(), pkt).await {
|
||||
warn!("[client {:?} send error ] {:?}", client_id, err);
|
||||
}
|
||||
},
|
||||
ServerStateAction::Disconnect => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn state_client_loop<STATE, S, R, E>(state: Arc<Mutex<STATE>>,
|
||||
server_state_receiver: async_std::channel::Receiver<ClientAction<ServerStateAction<S>, R>>) where
|
||||
STATE: ServerState<SendPacket=S, RecvPacket=R, PacketError=E> + Send + 'static,
|
||||
S: SendServerPacket + std::fmt::Debug + Send + 'static,
|
||||
R: RecvServerPacket + std::fmt::Debug + Send + 'static,
|
||||
E: std::fmt::Debug + Send,
|
||||
{
|
||||
async_std::task::spawn(async move {
|
||||
let mut clients = HashMap::new();
|
||||
|
||||
loop {
|
||||
let action = server_state_receiver.recv().await.unwrap();
|
||||
let mut state = state.lock().await;
|
||||
|
||||
match action {
|
||||
ClientAction::NewClient(client_id, sender) => {
|
||||
let actions = state.on_connect(client_id).await;
|
||||
match actions {
|
||||
Ok(actions) => {
|
||||
for action in actions {
|
||||
match action {
|
||||
OnConnect::Cipher((inc, outc)) => {
|
||||
sender.send(ServerStateAction::Cipher(inc, outc)).await.unwrap();
|
||||
},
|
||||
OnConnect::Packet(pkt) => {
|
||||
sender.send(ServerStateAction::Packet(pkt)).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("[client {:?} state on_connect error] {:?}", client_id, err);
|
||||
}
|
||||
}
|
||||
clients.insert(client_id, sender);
|
||||
},
|
||||
ClientAction::Packet(client_id, pkt) => {
|
||||
let pkts = state.handle(client_id, &pkt).await;
|
||||
match pkts {
|
||||
Ok(pkts) => {
|
||||
for (client_id, pkt) in pkts {
|
||||
if let Some(client) = clients.get_mut(&client_id) {
|
||||
client.send(ServerStateAction::Packet(pkt)).await.unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("[client {:?} state handler error] {:?}", client_id, err);
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientAction::Disconnect(client_id) => {
|
||||
let pkts = state.on_disconnect(client_id).await;
|
||||
match pkts {
|
||||
Ok(pkts) => {
|
||||
for (client_id, pkt) in pkts {
|
||||
if let Some(client) = clients.get_mut(&client_id) {
|
||||
client.send(ServerStateAction::Packet(pkt)).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(client) = clients.get_mut(&client_id) {
|
||||
client.send(ServerStateAction::Disconnect).await.unwrap();
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("[client {:?} state on_disconnect error] {:?}", client_id, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
pub fn client_accept_mainloop<STATE, S, R, E>(state: Arc<Mutex<STATE>>, client_port: u16) -> Pin<Box<dyn Future<Output = ()>>>
|
||||
where
|
||||
STATE: ServerState<SendPacket=S, RecvPacket=R, PacketError=E> + Send + 'static,
|
||||
S: SendServerPacket + std::fmt::Debug + Send + Sync + 'static,
|
||||
R: RecvServerPacket + std::fmt::Debug + Send + Sync + 'static,
|
||||
E: std::fmt::Debug + Send,
|
||||
{
|
||||
Box::pin(async_std::task::spawn(async move {
|
||||
let listener = async_std::net::TcpListener::bind(&std::net::SocketAddr::from((std::net::Ipv4Addr::new(0,0,0,0), client_port))).await.unwrap();
|
||||
let mut id = 0;
|
||||
|
||||
let (server_state_sender, server_state_receiver) = async_std::channel::bounded(1024);
|
||||
state_client_loop(state, server_state_receiver);
|
||||
|
||||
loop {
|
||||
let (sock, addr) = listener.accept().await.unwrap();
|
||||
id += 1;
|
||||
let client_id = crate::common::serverstate::ClientId(id);
|
||||
|
||||
info!("new client {:?} {:?} {:?}", client_id, sock, addr);
|
||||
|
||||
let (client_sender, client_receiver) = async_std::channel::bounded(64);
|
||||
let socket = Arc::new(sock);
|
||||
let cipher_in: Arc<Mutex<Box<dyn PSOCipher + Send>>> = Arc::new(Mutex::new(Box::new(NullCipher {})));
|
||||
let cipher_out: Arc<Mutex<Box<dyn PSOCipher + Send>>> = Arc::new(Mutex::new(Box::new(NullCipher {})));
|
||||
|
||||
client_recv_loop(client_id, socket.clone(), cipher_in.clone(), server_state_sender.clone(), client_sender);
|
||||
client_send_loop(client_id, socket.clone(), cipher_in.clone(), cipher_out.clone(), client_receiver);
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ use std::time::Duration;
|
||||
use std::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use log::{info, warn};
|
||||
use async_std::sync::{Arc, Mutex};
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
use async_std::io::prelude::{ReadExt, WriteExt};
|
||||
use std::collections::HashMap;
|
||||
use serde::Serialize;
|
||||
@ -10,10 +10,16 @@ use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::common::interserver::{ServerId, InterserverActor};
|
||||
|
||||
use libpso::crypto::{PSOCipher, NullCipher, CipherError};
|
||||
use crate::common::serverstate::{ServerState, SendServerPacket, RecvServerPacket};
|
||||
use crate::login::character::CharacterServerState;
|
||||
use crate::ship::ship::ShipServerState;
|
||||
//use crate::ship::ship::ShipServerState;
|
||||
use crate::entity::gateway::entitygateway::EntityGateway;
|
||||
|
||||
use async_std::channel;
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MessageReceiverError {
|
||||
//InvalidSize,
|
||||
@ -33,7 +39,7 @@ impl MessageReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
async fn recv<R: DeserializeOwned + std::fmt::Debug + Send>(&mut self) -> Result<R, MessageReceiverError> {
|
||||
async fn recv<R: serde::de::DeserializeOwned + std::fmt::Debug>(&mut self) -> Result<R, MessageReceiverError> {
|
||||
let mut size_buf = [0u8; 4];
|
||||
self.socket.read_exact(&mut size_buf).await.map_err(|_| MessageReceiverError::Disconnected)?;
|
||||
let size = u32::from_le_bytes(size_buf) as usize;
|
||||
@ -47,214 +53,181 @@ impl MessageReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum InterserverInputAction<S, R> {
|
||||
NewConnection(ServerId, async_std::channel::Sender<S>),
|
||||
Message(ServerId, R),
|
||||
Disconnect(ServerId),
|
||||
}
|
||||
|
||||
async fn interserver_state_loop<A, S, R>(state: Arc<Mutex<A>>, action_receiver: async_std::channel::Receiver<InterserverInputAction<S, R>>)
|
||||
async fn interserver_recv_loop<STATE, S, R, E>(mut state: STATE, server_id: ServerId, socket: async_std::net::TcpStream, ships: Arc<RwLock<HashMap<ServerId, channel::Sender<S>>>>)
|
||||
where
|
||||
A: InterserverActor<SendMessage=S, RecvMessage=R, Error=()> + Send + 'static,
|
||||
S: Serialize + Send + 'static,
|
||||
R: DeserializeOwned + Send + 'static,
|
||||
STATE: InterserverActor<SendMessage=S, RecvMessage=R, Error=E> + Send,
|
||||
S: serde::Serialize + Debug + Send,
|
||||
R: serde::de::DeserializeOwned + Debug + Send,
|
||||
E: Debug + Send,
|
||||
{
|
||||
async_std::task::spawn(async move {
|
||||
let mut ships = HashMap::new();
|
||||
let mut msg_receiver = MessageReceiver::new(socket);
|
||||
|
||||
loop {
|
||||
info!("interserver loop");
|
||||
let action = match action_receiver.recv().await {
|
||||
Ok(action) => action,
|
||||
Err(err) => {
|
||||
warn!("error in iterserver state loop {:?}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut state = state.lock().await;
|
||||
|
||||
match action {
|
||||
InterserverInputAction::NewConnection(server_id, ship_action_sender) => {
|
||||
ships.insert(server_id, ship_action_sender);
|
||||
for (server, action) in state.on_connect(server_id).await {
|
||||
if let Some(sender) = ships.get_mut(&server) {
|
||||
sender.send(action).await.unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
InterserverInputAction::Message(server_id, message) => {
|
||||
let actions = state.action(server_id, message).await;
|
||||
match actions {
|
||||
Ok(actions) => {
|
||||
for (server, action) in actions{
|
||||
if let Some(sender) = ships.get_mut(&server) {
|
||||
sender.send(action).await.unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("[server {:?} state handler error] {:?}", server_id, err);
|
||||
}
|
||||
}
|
||||
},
|
||||
InterserverInputAction::Disconnect(server_id) => {
|
||||
let actions = state.on_disconnect(server_id).await;
|
||||
ships.remove(&server_id);
|
||||
for (server, action) in actions {
|
||||
if let Some(sender) = ships.get_mut(&server) {
|
||||
sender.send(action).await.unwrap();
|
||||
loop {
|
||||
match msg_receiver.recv::<R>().await {
|
||||
Ok(msg) => {
|
||||
info!("[interserver recv {:?}] {:?}", server_id, msg);
|
||||
match state.on_action(server_id, msg).await {
|
||||
Ok(response) => {
|
||||
for resp in response {
|
||||
ships
|
||||
.read()
|
||||
.await
|
||||
.get(&resp.0)
|
||||
.unwrap()
|
||||
.send(resp.1)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("[interserver recv {:?}] error {:?}", server_id, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn login_recv_loop<S, R>(server_id: ServerId,
|
||||
socket: async_std::net::TcpStream,
|
||||
state_loop_sender: async_std::channel::Sender<InterserverInputAction<S, R>>,
|
||||
output_loop_sender: async_std::channel::Sender<S>)
|
||||
where
|
||||
S: Serialize + std::fmt::Debug + Send + 'static,
|
||||
R: DeserializeOwned + std::fmt::Debug + Send + 'static,
|
||||
{
|
||||
async_std::task::spawn(async move {
|
||||
state_loop_sender.send(InterserverInputAction::NewConnection(server_id, output_loop_sender)).await.unwrap();
|
||||
let mut msg_receiver = MessageReceiver::new(socket);
|
||||
|
||||
loop {
|
||||
info!("login recv loop");
|
||||
match msg_receiver.recv().await {
|
||||
Ok(msg) => {
|
||||
info!("[login recv loop msg] {:?}", msg);
|
||||
state_loop_sender.send(InterserverInputAction::Message(server_id, msg)).await.unwrap();
|
||||
},
|
||||
Err(err) => {
|
||||
if let MessageReceiverError::Disconnected = err {
|
||||
info!("[login recv loop disconnect] {:?}", server_id);
|
||||
state_loop_sender.send(InterserverInputAction::Disconnect(server_id)).await.unwrap();
|
||||
break;
|
||||
}
|
||||
info!("[login recv loop err] {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn interserver_send_loop<S>(server_id: ServerId,
|
||||
mut socket: async_std::net::TcpStream,
|
||||
output_loop_receiver: async_std::channel::Receiver<S>)
|
||||
where
|
||||
S: Serialize + std::fmt::Debug + Send + 'static,
|
||||
{
|
||||
async_std::task::spawn(async move {
|
||||
loop {
|
||||
info!("login send loop");
|
||||
match output_loop_receiver.recv().await {
|
||||
Ok(msg) => {
|
||||
let payload = serde_json::to_string(&msg);
|
||||
if let Ok(payload) = payload {
|
||||
let len_bytes = u32::to_le_bytes(payload.len() as u32);
|
||||
|
||||
if let Err(err) = socket.write_all(&len_bytes).await {
|
||||
warn!("interserver send failed: {:?}", err);
|
||||
break;
|
||||
}
|
||||
if let Err(err) = socket.write_all(payload.as_bytes()).await {
|
||||
warn!("intserserver send failed: {:?}", err);
|
||||
break;
|
||||
},
|
||||
Err(err) => {
|
||||
if let MessageReceiverError::Disconnected = err {
|
||||
info!("[interserver recv {:?}] disconnected", server_id);
|
||||
for (_, _sender) in ships.read().await.iter() {
|
||||
for pkt in state.on_disconnect(server_id).await {
|
||||
ships
|
||||
.read()
|
||||
.await
|
||||
.get(&pkt.0)
|
||||
.unwrap()
|
||||
.send(pkt.1)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("error in send_loop: {:?}, {:?}", server_id, err);
|
||||
ships
|
||||
.write()
|
||||
.await
|
||||
.remove(&server_id);
|
||||
break;
|
||||
}
|
||||
info!("[interserver recv {:?}] error {:?}", server_id, err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn interserver_send_loop<S>(server_id: ServerId, mut socket: async_std::net::TcpStream, to_send: channel::Receiver<S>)
|
||||
where
|
||||
S: serde::Serialize + std::fmt::Debug,
|
||||
{
|
||||
loop {
|
||||
let msg = to_send.recv().await.unwrap();
|
||||
let payload = serde_json::to_string(&msg);
|
||||
|
||||
|
||||
pub fn login_listen_mainloop<EG: EntityGateway + Clone + 'static>(state: Arc<Mutex<CharacterServerState<EG>>>, port: u16) -> Pin<Box<dyn Future<Output = ()>>> {
|
||||
Box::pin(async_std::task::spawn(async move {
|
||||
let listener = async_std::net::TcpListener::bind(&std::net::SocketAddr::from((std::net::Ipv4Addr::new(0,0,0,0), port))).await.unwrap();
|
||||
let mut id = 0;
|
||||
|
||||
let (server_state_sender, server_state_receiver) = async_std::channel::bounded(1024);
|
||||
interserver_state_loop(state.clone(), server_state_receiver).await;
|
||||
|
||||
loop {
|
||||
let (socket, addr) = listener.accept().await.unwrap();
|
||||
info!("new ship server: {:?} {:?}", socket, addr);
|
||||
|
||||
id += 1;
|
||||
let server_id = crate::common::interserver::ServerId(id);
|
||||
let (client_sender, client_receiver) = async_std::channel::bounded(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)).unwrap();
|
||||
}))
|
||||
if let Ok(payload) = payload {
|
||||
let len_bytes = u32::to_le_bytes(payload.len() as u32);
|
||||
if let Err(err) = socket.write_all(&len_bytes).await {
|
||||
warn!("[interserver send {:?}] failed: {:?}", server_id, err);
|
||||
break;
|
||||
}
|
||||
if let Err(err) = socket.write_all(payload.as_bytes()).await {
|
||||
warn!("[interserver send {:?}] failed: {:?}", server_id, err);
|
||||
break;
|
||||
}
|
||||
|
||||
login_recv_loop(server_id, socket.clone(), server_state_sender.clone(), client_sender).await;
|
||||
interserver_send_loop(server_id, socket.clone(), client_receiver).await;
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ship_connect_mainloop<EG: EntityGateway + Clone + '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 {
|
||||
let mut id = 0;
|
||||
let (server_state_sender, server_state_receiver) = async_std::channel::bounded(1024);
|
||||
pub async fn run_interserver_listen<STATE, S, R, E>(mut state: STATE, port: u16)
|
||||
where
|
||||
STATE: InterserverActor<SendMessage=S, RecvMessage=R, Error=E> + Send + 'static,
|
||||
S: serde::Serialize + Debug + Send + 'static,
|
||||
R: serde::de::DeserializeOwned + Debug + Send,
|
||||
E: Debug + Send,
|
||||
{
|
||||
let listener = async_std::net::TcpListener::bind(&std::net::SocketAddr::from((std::net::Ipv4Addr::new(0,0,0,0), port))).await.unwrap();
|
||||
let mut id = 0;
|
||||
let ships = Arc::new(RwLock::new(HashMap::new()));
|
||||
|
||||
interserver_state_loop(state.clone(), server_state_receiver).await;
|
||||
loop {
|
||||
let (socket, addr) = listener.accept().await.unwrap();
|
||||
info!("[interserver listen] new server: {:?} {:?}", socket, addr);
|
||||
|
||||
loop {
|
||||
info!("trying to connect to loginserv");
|
||||
let socket = match async_std::net::TcpStream::connect((ip, port)).await {
|
||||
Ok(socket) => socket,
|
||||
Err(err) => {
|
||||
info!("err trying to connect to loginserv {:?}", err);
|
||||
async_std::task::sleep(Duration::from_secs(10)).await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
id += 1;
|
||||
let server_id = crate::common::interserver::ServerId(id);
|
||||
info!("found loginserv: {:?} {:?}", server_id, socket);
|
||||
let (client_sender, client_receiver) = async_std::channel::bounded(64);
|
||||
id += 1;
|
||||
let server_id = crate::common::interserver::ServerId(id);
|
||||
let (client_tx, client_rx) = async_std::channel::unbounded();
|
||||
state.set_sender(server_id, client_tx.clone()).await;
|
||||
|
||||
{
|
||||
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)).unwrap();
|
||||
}))
|
||||
ships
|
||||
.write()
|
||||
.await
|
||||
.insert(server_id, client_tx.clone());
|
||||
|
||||
for msg in state.on_connect(server_id).await {
|
||||
if let Some(ship_sender) = ships.read().await.get(&msg.0) {
|
||||
ship_sender.send(msg.1).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
login_recv_loop(server_id, socket.clone(), server_state_sender.clone(), client_sender).await;
|
||||
interserver_send_loop(server_id, socket.clone(), client_receiver).await;
|
||||
let rstate = state.clone();
|
||||
let rsocket = socket.clone();
|
||||
let rships = ships.clone();
|
||||
async_std::task::spawn(async move {
|
||||
interserver_recv_loop(rstate, server_id, rsocket, rships).await;
|
||||
});
|
||||
async_std::task::spawn(async move {
|
||||
interserver_send_loop(server_id, socket, client_rx).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut buf = [0u8; 1];
|
||||
loop {
|
||||
let peek = socket.peek(&mut buf).await;
|
||||
match peek {
|
||||
Ok(len) if len == 0 => {
|
||||
break
|
||||
},
|
||||
_ => {
|
||||
}
|
||||
pub async fn run_interserver_connect<STATE, S, R, E>(mut state: STATE, ip: std::net::Ipv4Addr, port: u16)
|
||||
where
|
||||
STATE: InterserverActor<SendMessage=S, RecvMessage=R, Error=E> + Send + 'static,
|
||||
S: serde::Serialize + Debug + Send + 'static,
|
||||
R: serde::de::DeserializeOwned + Debug + Send,
|
||||
E: Debug + Send,
|
||||
{
|
||||
let mut id = 0;
|
||||
|
||||
loop {
|
||||
info!("[interserver connect] trying to connect to server");
|
||||
let socket = match async_std::net::TcpStream::connect((ip, port)).await {
|
||||
Ok(socket) => socket,
|
||||
Err(err) => {
|
||||
info!("err trying to connect to loginserv {:?}", err);
|
||||
async_std::task::sleep(std::time::Duration::from_secs(10)).await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
id += 1;
|
||||
let server_id = crate::common::interserver::ServerId(id);
|
||||
info!("[interserver connect] found loginserv: {:?} {:?}", server_id, socket);
|
||||
|
||||
let (client_tx, client_rx) = async_std::channel::unbounded();
|
||||
state.set_sender(server_id, client_tx.clone()).await;
|
||||
|
||||
for msg in state.on_connect(server_id).await {
|
||||
client_tx.send(msg.1).await.unwrap();
|
||||
}
|
||||
|
||||
let other_server = vec![(server_id, client_tx.clone())].into_iter().collect();
|
||||
let rstate = state.clone();
|
||||
let rsocket = socket.clone();
|
||||
async_std::task::spawn(async move {
|
||||
interserver_recv_loop(rstate, server_id, rsocket, Arc::new(RwLock::new(other_server))).await;
|
||||
});
|
||||
let ssocket = socket.clone();
|
||||
async_std::task::spawn(async move {
|
||||
interserver_send_loop(server_id, ssocket, client_rx).await;
|
||||
});
|
||||
|
||||
let mut buf = [0u8; 1];
|
||||
loop {
|
||||
let peek = socket.peek(&mut buf).await;
|
||||
match peek {
|
||||
Ok(len) if len == 0 => {
|
||||
break
|
||||
},
|
||||
_ => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,45 +1,6 @@
|
||||
#![allow(unused_imports)]
|
||||
mod client;
|
||||
mod interserver;
|
||||
|
||||
use std::pin::Pin;
|
||||
use futures::future::{Future, join_all, FutureExt};
|
||||
use async_std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::common::mainloop::client::client_accept_mainloop;
|
||||
use crate::common::mainloop::interserver::{ship_connect_mainloop, login_listen_mainloop};
|
||||
pub use crate::common::mainloop::client::NetworkError;
|
||||
|
||||
use crate::patch::patch::PatchServerState;
|
||||
use crate::login::login::LoginServerState;
|
||||
use crate::login::character::CharacterServerState;
|
||||
use crate::ship::ship::ShipServerState;
|
||||
use crate::entity::gateway::entitygateway::EntityGateway;
|
||||
|
||||
|
||||
|
||||
pub fn patch_mainloop(patch_state: PatchServerState, patch_port: u16) -> Pin<Box<dyn Future<Output = ()>>> {
|
||||
let patch_state = Arc::new(Mutex::new(patch_state));
|
||||
let client_mainloop = client_accept_mainloop(patch_state, patch_port);
|
||||
Box::pin(client_mainloop)
|
||||
}
|
||||
|
||||
pub fn login_mainloop<EG: EntityGateway + Clone + 'static>(login_state: LoginServerState<EG>, login_port: u16) -> Pin<Box<dyn Future<Output = ()>>> {
|
||||
let login_state = Arc::new(Mutex::new(login_state));
|
||||
let client_mainloop = client_accept_mainloop(login_state, login_port);
|
||||
Box::pin(client_mainloop)
|
||||
}
|
||||
|
||||
pub fn character_mainloop<EG: EntityGateway + Clone + 'static>(character_state: CharacterServerState<EG>, character_port: u16, comm_port: u16) -> Pin<Box<dyn Future<Output = ()>>> {
|
||||
let character_state = Arc::new(Mutex::new(character_state));
|
||||
let client_mainloop = client_accept_mainloop(character_state.clone(), character_port);
|
||||
let ship_communication_mainloop = login_listen_mainloop(character_state, comm_port);
|
||||
Box::pin(join_all(vec![client_mainloop, ship_communication_mainloop]).map(|_| ()))
|
||||
}
|
||||
|
||||
|
||||
pub fn ship_mainloop<EG: EntityGateway + Clone + 'static>(ship_state: ShipServerState<EG>, ship_port: u16, comm_ip: std::net::Ipv4Addr, comm_port: u16) -> Pin<Box<dyn Future<Output = ()>>> {
|
||||
let ship_state = Arc::new(Mutex::new(ship_state));
|
||||
let client_mainloop = client_accept_mainloop(ship_state.clone(), ship_port);
|
||||
let login_communication_mainloop = ship_connect_mainloop(ship_state, comm_ip, comm_port);
|
||||
Box::pin(join_all(vec![client_mainloop, login_communication_mainloop]).map(|_| ()))
|
||||
}
|
||||
pub use self::client::*;
|
||||
pub use self::interserver::*;
|
||||
|
@ -4,9 +4,10 @@ use libpso::crypto::PSOCipher;
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)]
|
||||
pub struct ClientId(pub usize);
|
||||
|
||||
pub enum OnConnect<S: SendServerPacket> {
|
||||
pub enum OnConnect<S: SendServerPacket, C: PSOCipher> {
|
||||
Packet(S),
|
||||
Cipher((Box<dyn PSOCipher + Send + Sync>, Box<dyn PSOCipher + Send + Sync>)),
|
||||
Cipher(C, C),
|
||||
//Cipher((Box<dyn PSOCipher + Send + Sync>, Box<dyn PSOCipher + Send + Sync>)),
|
||||
}
|
||||
|
||||
pub trait RecvServerPacket: Sized + Sync {
|
||||
@ -19,14 +20,14 @@ pub trait SendServerPacket: Sized + Sync {
|
||||
|
||||
// TODO: rename this trait, this isn't the state but the actionability of the state re: the client
|
||||
#[async_trait::async_trait]
|
||||
pub trait ServerState {
|
||||
pub trait ServerState: Clone {
|
||||
type SendPacket: SendServerPacket;
|
||||
type RecvPacket: RecvServerPacket;
|
||||
type Cipher: PSOCipher;
|
||||
type PacketError;
|
||||
|
||||
async fn on_connect(&mut self, id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, Self::PacketError>;
|
||||
async fn handle(&mut self, id: ClientId, pkt: &Self::RecvPacket)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, Self::SendPacket)> + Send>, Self::PacketError>;
|
||||
async fn on_connect(&mut self, id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket, Self::Cipher>>, Self::PacketError>;
|
||||
async fn handle(&mut self, id: ClientId, pkt: Self::RecvPacket) -> Result<Vec<(ClientId, Self::SendPacket)>, Self::PacketError>;
|
||||
//-> Result<Box<dyn Iterator<Item = (ClientId, Self::SendPacket)>>, Self::PacketError>;
|
||||
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, Self::SendPacket)>, Self::PacketError>;
|
||||
}
|
||||
|
||||
|
@ -675,18 +675,18 @@ impl From<PgItemNoteDetail> for ItemNote {
|
||||
fn from(other: PgItemNoteDetail) -> ItemNote {
|
||||
match other {
|
||||
PgItemNoteDetail::CharacterCreation{character_id} => ItemNote::CharacterCreation {
|
||||
character_id: CharacterEntityId(character_id as u32),
|
||||
character_id: CharacterEntityId(character_id),
|
||||
},
|
||||
PgItemNoteDetail::EnemyDrop{character_id, map_area, x, y, z} => ItemNote::EnemyDrop {
|
||||
character_id: CharacterEntityId(character_id as u32),
|
||||
character_id: CharacterEntityId(character_id),
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
PgItemNoteDetail::Pickup{character_id} => ItemNote::Pickup {
|
||||
character_id: CharacterEntityId(character_id as u32),
|
||||
character_id: CharacterEntityId(character_id),
|
||||
},
|
||||
PgItemNoteDetail::PlayerDrop{character_id, map_area, x, y, z} => ItemNote::PlayerDrop {
|
||||
character_id: CharacterEntityId(character_id as u32),
|
||||
character_id: CharacterEntityId(character_id),
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
@ -699,16 +699,16 @@ impl From<PgItemNoteDetail> for ItemNote {
|
||||
},
|
||||
PgItemNoteDetail::SoldToShop => ItemNote::SoldToShop,
|
||||
PgItemNoteDetail::Trade {trade_id, character_to, character_from} => ItemNote::Trade {
|
||||
trade_id: TradeId(trade_id as u32),
|
||||
character_to: CharacterEntityId(character_to as u32),
|
||||
character_from: CharacterEntityId(character_from as u32),
|
||||
trade_id: TradeId(trade_id),
|
||||
character_to: CharacterEntityId(character_to),
|
||||
character_from: CharacterEntityId(character_from),
|
||||
},
|
||||
PgItemNoteDetail::Withdraw{character_id, bank} => ItemNote::Withdraw {
|
||||
character_id: CharacterEntityId(character_id as u32),
|
||||
character_id: CharacterEntityId(character_id),
|
||||
bank: BankName(bank),
|
||||
},
|
||||
PgItemNoteDetail::Deposit{character_id, bank} => ItemNote::Deposit {
|
||||
character_id: CharacterEntityId(character_id as u32),
|
||||
character_id: CharacterEntityId(character_id),
|
||||
bank: BankName(bank),
|
||||
},
|
||||
}
|
||||
|
@ -162,10 +162,10 @@ async fn save_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> R
|
||||
sqlx::query("UPDATE user_accounts set username=$1, password=$2, banned=$3, muted=$4, flags=$5 where id=$6")
|
||||
.bind(&user.username)
|
||||
.bind(&user.password)
|
||||
.bind(&user.banned_until)
|
||||
.bind(&user.muted_until)
|
||||
.bind(&user.flags)
|
||||
.bind(&user.id.0)
|
||||
.bind(user.banned_until)
|
||||
.bind(user.muted_until)
|
||||
.bind(user.flags)
|
||||
.bind(user.id.0)
|
||||
.execute(conn).await?;
|
||||
Ok(())
|
||||
}
|
||||
@ -200,11 +200,11 @@ async fn save_user_settings(conn: &mut sqlx::PgConnection, settings: &UserSettin
|
||||
.bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
|
||||
.bind(&settings.settings.keyboard_config.to_vec())
|
||||
.bind(&settings.settings.gamepad_config.to_vec())
|
||||
.bind(&settings.settings.option_flags)
|
||||
.bind(settings.settings.option_flags)
|
||||
.bind(&settings.settings.shortcuts.to_vec())
|
||||
.bind(&settings.settings.symbol_chats.to_vec())
|
||||
.bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
|
||||
.bind(&settings.id.0)
|
||||
.bind(settings.id.0)
|
||||
.fetch_one(conn).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -3,7 +3,10 @@
|
||||
#![feature(drain_filter)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(once_cell)]
|
||||
#![feature(pin_macro)]
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate fix_hidden_lifetime_bug;
|
||||
|
||||
|
||||
|
@ -2,6 +2,9 @@
|
||||
use std::io::Read;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
use async_std::channel;
|
||||
|
||||
use rand::Rng;
|
||||
use crc::{crc32, Hasher32};
|
||||
|
||||
@ -15,7 +18,7 @@ use libpso::character::character;
|
||||
use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
|
||||
use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
|
||||
use crate::common::interserver::{ServerId, InterserverActor, LoginMessage, ShipMessage, Ship};
|
||||
use crate::common::leveltable::CharacterLevelTable;
|
||||
use crate::common::leveltable::LEVEL_TABLE;
|
||||
use libpso::{utf8_to_array, utf8_to_utf16_array};
|
||||
|
||||
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||
@ -179,22 +182,22 @@ struct ConnectedClient {
|
||||
expires: Option<chrono::DateTime<chrono::Utc>>,
|
||||
}
|
||||
|
||||
pub struct CharacterServerState<EG: EntityGateway> {
|
||||
#[derive(Clone)]
|
||||
pub struct CharacterServerState<EG: EntityGateway + Clone> {
|
||||
entity_gateway: EG,
|
||||
param_header: ParamDataHeader,
|
||||
param_data: Vec<u8>,
|
||||
clients: HashMap<ClientId, ClientState>,
|
||||
ships: BTreeMap<ServerId, Ship>,
|
||||
level_table: CharacterLevelTable,
|
||||
param_data: Arc<Vec<u8>>,
|
||||
clients: Arc<RwLock<HashMap<ClientId, ClientState>>>,
|
||||
ships: Arc<RwLock<BTreeMap<ServerId, Ship>>>,
|
||||
auth_token: AuthToken,
|
||||
|
||||
connected_clients: BTreeMap<UserAccountId, ConnectedClient>,
|
||||
authenticated_ships: BTreeSet<ServerId>,
|
||||
ship_sender: BTreeMap<ServerId, Box<dyn Fn(LoginMessage) + Send>>,
|
||||
connected_clients: Arc<RwLock<BTreeMap<UserAccountId, ConnectedClient>>>,
|
||||
authenticated_ships: Arc<RwLock<BTreeSet<ServerId>>>,
|
||||
ship_sender: Arc<RwLock<BTreeMap<ServerId, channel::Sender<LoginMessage>>>>,
|
||||
}
|
||||
|
||||
|
||||
async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccountEntity, preview: &CharacterPreview) -> Result<(), anyhow::Error> {
|
||||
async fn new_character<EG: EntityGateway + Clone>(entity_gateway: &mut EG, user: &UserAccountEntity, preview: &CharacterPreview) -> Result<(), anyhow::Error> {
|
||||
let mut character = new_character_from_preview(user, preview);
|
||||
match character.char_class {
|
||||
CharacterClass::FOmar | CharacterClass::FOmarl| CharacterClass::FOnewm | CharacterClass::FOnewearl => character.techs.set_tech(Technique::Foie, TechLevel(1)),
|
||||
@ -306,32 +309,28 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
|
||||
}
|
||||
|
||||
|
||||
impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
impl<EG: EntityGateway + Clone> CharacterServerState<EG> {
|
||||
pub fn new(entity_gateway: EG, auth_token: AuthToken) -> CharacterServerState<EG> {
|
||||
let (param_header, param_data) = generate_param_data("data/param/");
|
||||
|
||||
CharacterServerState {
|
||||
entity_gateway,
|
||||
param_header,
|
||||
param_data,
|
||||
clients: HashMap::new(),
|
||||
ships: BTreeMap::new(),
|
||||
level_table: CharacterLevelTable::default(),
|
||||
param_data: Arc::new(param_data),
|
||||
clients: Default::default(),
|
||||
ships: Default::default(),
|
||||
//level_table: CharacterLevelTable::default(),
|
||||
auth_token,
|
||||
authenticated_ships: BTreeSet::new(),
|
||||
ship_sender: BTreeMap::new(),
|
||||
connected_clients: BTreeMap::new(),
|
||||
authenticated_ships: Default::default(),
|
||||
ship_sender: Default::default(),
|
||||
connected_clients: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
match get_login_status(&mut self.entity_gateway, pkt).await {
|
||||
Ok(user) => {
|
||||
if let Some(connected_client) = self.connected_clients.get(&user.id) {
|
||||
if let Some(connected_client) = self.connected_clients.read().await.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()))]);
|
||||
@ -344,11 +343,12 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
|
||||
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
|
||||
response.guildcard = user.guildcard;
|
||||
response.team_id = user.team_id.map_or(0, |ti| ti) as u32;
|
||||
response.team_id = user.team_id.map_or(0, |ti| ti);
|
||||
|
||||
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
||||
let mut client = self.clients.write().await;
|
||||
let client = client.get_mut(&id).ok_or_else(|| CharacterError::ClientNotFound(id))?;
|
||||
|
||||
self.connected_clients.insert(user.id, ConnectedClient {
|
||||
self.connected_clients.write().await.insert(user.id, ConnectedClient {
|
||||
ship_id: None,
|
||||
expires: None, //Some(chrono::Utc::now() + chrono::Duration::minutes(1)),
|
||||
});
|
||||
@ -363,9 +363,9 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_ship_list(&mut self, _id: ClientId, _pkt: &Login) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
async 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)| {
|
||||
SendCharacterPacket::ShipList(ShipList::new(self.ships.read().await.iter().map(|(i, s)| {
|
||||
ShipListEntry {
|
||||
menu: SHIP_MENU_ID,
|
||||
item: i.0 as u32,
|
||||
@ -377,7 +377,8 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
}
|
||||
|
||||
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 mut client = self.clients.write().await;
|
||||
let client = client.get_mut(&id).ok_or_else(|| CharacterError::ClientNotFound(id))?;
|
||||
let user = client.user.as_ref().unwrap();
|
||||
|
||||
let settings = match self.entity_gateway.get_user_settings_by_user(user).await {
|
||||
@ -396,7 +397,8 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
}
|
||||
|
||||
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))?;
|
||||
let mut client = self.clients.write().await;
|
||||
let client = client.get_mut(&id).ok_or_else(|| 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)?);
|
||||
}
|
||||
@ -404,7 +406,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
if select.reason == 0 {
|
||||
let chars = client.characters.as_ref().unwrap();
|
||||
Ok(if let Some(char) = &chars[select.slot as usize] {
|
||||
let (level, _stats) = self.level_table.get_stats_from_exp(char.char_class, char.exp);
|
||||
let (level, _stats) = LEVEL_TABLE.get_stats_from_exp(char.char_class, char.exp);
|
||||
vec![SendCharacterPacket::CharacterPreview(CharacterPreview {
|
||||
slot: select.slot,
|
||||
character: SelectScreenCharacterBuilder::new()
|
||||
@ -442,7 +444,8 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
}
|
||||
|
||||
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 mut client = self.clients.write().await;
|
||||
let client = client.get_mut(&id).ok_or_else(|| 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)?;
|
||||
|
||||
let bytes = guildcard_data.guildcard.as_bytes();
|
||||
@ -453,15 +456,16 @@ 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>, anyhow::Error> {
|
||||
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
||||
async fn guildcard_data_chunk(&mut self, id: ClientId, chunk: u32, again: u32) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
let mut client = self.clients.write().await;
|
||||
let client = client.get_mut(&id).ok_or_else(|| CharacterError::ClientNotFound(id))?;
|
||||
Ok(if again != 0 {
|
||||
let start = chunk as usize * GUILD_CARD_CHUNK_SIZE;
|
||||
let len = std::cmp::min(GUILD_CARD_CHUNK_SIZE, client.guildcard_data_buffer.as_ref().unwrap().len() as usize - start);
|
||||
let len = std::cmp::min(GUILD_CARD_CHUNK_SIZE, client.guildcard_data_buffer.as_ref().unwrap().len() - start);
|
||||
let end = start + len;
|
||||
|
||||
let mut buf = [0u8; GUILD_CARD_CHUNK_SIZE as usize];
|
||||
buf[..len as usize].copy_from_slice(&client.guildcard_data_buffer.as_ref().unwrap()[start..end]);
|
||||
let mut buf = [0u8; GUILD_CARD_CHUNK_SIZE];
|
||||
buf[..len].copy_from_slice(&client.guildcard_data_buffer.as_ref().unwrap()[start..end]);
|
||||
|
||||
vec![SendCharacterPacket::GuildcardDataChunk(Box::new(GuildcardDataChunk::new(chunk, buf, len)))]
|
||||
} else {
|
||||
@ -470,15 +474,17 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
}
|
||||
|
||||
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 client = self.clients.write().await;
|
||||
let client = client.get_mut(&id).ok_or_else(|| CharacterError::ClientNotFound(id))?;
|
||||
let mut user = client.user.as_mut().unwrap();
|
||||
user.flags = setflag.flags;
|
||||
self.entity_gateway.save_user(user).await.unwrap();
|
||||
Ok(None.into_iter())
|
||||
}
|
||||
|
||||
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))?;
|
||||
async fn param_data_chunk_request(&mut self, id: ClientId, _request: &ParamDataChunkRequest) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
let mut client = self.clients.write().await;
|
||||
let client = client.get_mut(&id).ok_or_else(|| CharacterError::ClientNotFound(id))?;
|
||||
let chunk = client.param_index;
|
||||
client.param_index += 1;
|
||||
|
||||
@ -499,7 +505,8 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
|
||||
// TODO: move USERFLAGS over to SessionAction
|
||||
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 client = self.clients.write().await;
|
||||
let client = client.get_mut(&id).ok_or_else(|| CharacterError::ClientNotFound(id))?;
|
||||
let mut user = client.user.as_mut().unwrap();
|
||||
if user.flags == USERFLAG_NEWCHAR {
|
||||
new_character(&mut self.entity_gateway, user, preview).await?
|
||||
@ -522,26 +529,30 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
])
|
||||
}
|
||||
|
||||
fn select_ship(&mut self, id: ClientId, menuselect: &MenuSelect) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
async fn select_ship(&mut self, id: ClientId, menuselect: &MenuSelect) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
if menuselect.menu != SHIP_MENU_ID {
|
||||
return Err(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item).into());
|
||||
}
|
||||
|
||||
if let Some(client) = self.clients.get(&id) {
|
||||
if let Some(client) = self.clients.read().await.get(&id) {
|
||||
if let Some(user) = &client.user {
|
||||
if let Some(cc) = self.connected_clients.get_mut(&user.id) {
|
||||
if let Some(cc) = self.connected_clients.write().await.get_mut(&user.id) {
|
||||
cc.ship_id = Some(ServerId(menuselect.item as usize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ship = self.ships.get(&ServerId(menuselect.item as usize))
|
||||
.ok_or(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?;
|
||||
let ship = self.ships.read().await;
|
||||
let ship = ship.get(&ServerId(menuselect.item as usize))
|
||||
.ok_or_else(|| CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?;
|
||||
Ok(vec![SendCharacterPacket::RedirectClient(RedirectClient::new(u32::from_le_bytes(ship.ip.octets()), ship.port))])
|
||||
}
|
||||
|
||||
fn ship_detail(&mut self, menudetail: &MenuDetail) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
let players = self.connected_clients.iter()
|
||||
async fn ship_detail(&mut self, menudetail: &MenuDetail) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
|
||||
let players = self.connected_clients
|
||||
.read()
|
||||
.await
|
||||
.iter()
|
||||
.filter(|(_, client)| {
|
||||
client.ship_id == Some(ServerId(menudetail.item as usize))
|
||||
})
|
||||
@ -552,13 +563,14 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
|
||||
impl<EG: EntityGateway + Clone> ServerState for CharacterServerState<EG> {
|
||||
type SendPacket = SendCharacterPacket;
|
||||
type RecvPacket = RecvCharacterPacket;
|
||||
type Cipher = PSOBBCipher;
|
||||
type PacketError = anyhow::Error;
|
||||
|
||||
async fn on_connect(&mut self, id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, anyhow::Error> {
|
||||
self.clients.insert(id, ClientState::new());
|
||||
async fn on_connect(&mut self, id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket, Self::Cipher>>, anyhow::Error> {
|
||||
self.clients.write().await.insert(id, ClientState::new());
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
@ -568,65 +580,66 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
|
||||
rng.fill(&mut client_key[..]);
|
||||
|
||||
Ok(vec![OnConnect::Packet(SendCharacterPacket::LoginWelcome(LoginWelcome::new(server_key, client_key))),
|
||||
OnConnect::Cipher((Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key)),
|
||||
Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key))))
|
||||
OnConnect::Cipher(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key),
|
||||
PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key))
|
||||
//OnConnect::Cipher((Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key)),
|
||||
// Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key))))
|
||||
])
|
||||
}
|
||||
|
||||
async fn handle(&mut self, id: ClientId, pkt: &RecvCharacterPacket)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendCharacterPacket)> + Send>, anyhow::Error> {
|
||||
async fn handle(&mut self, id: ClientId, pkt: RecvCharacterPacket) -> Result<Vec<(ClientId, SendCharacterPacket)>, anyhow::Error> {
|
||||
Ok(match pkt {
|
||||
RecvCharacterPacket::Login(login) => {
|
||||
if login.session.action == SessionAction::SelectCharacter {
|
||||
Box::new(self.send_ship_list(id, login)?.into_iter().map(move |pkt| (id, pkt)))
|
||||
self.send_ship_list(id, &login).await?.into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
}
|
||||
else {
|
||||
Box::new(self.validate_login(id, login).await?.into_iter().map(move |pkt| (id, pkt)))
|
||||
self.validate_login(id, &login).await?.into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
}
|
||||
},
|
||||
RecvCharacterPacket::RequestSettings(_req) => {
|
||||
Box::new(self.get_settings(id).await?.into_iter().map(move |pkt| (id, pkt)))
|
||||
self.get_settings(id).await?.into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
},
|
||||
RecvCharacterPacket::CharSelect(sel) => {
|
||||
Box::new(self.char_select(id, sel).await?.into_iter().map(move |pkt| (id, pkt)))
|
||||
self.char_select(id, &sel).await?.into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
},
|
||||
RecvCharacterPacket::Checksum(_checksum) => {
|
||||
Box::new(self.validate_checksum().into_iter().map(move |pkt| (id, pkt)))
|
||||
self.validate_checksum().into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
},
|
||||
RecvCharacterPacket::GuildcardDataRequest(_request) => {
|
||||
Box::new(self.guildcard_data_header(id).await?.into_iter().map(move |pkt| (id, pkt)))
|
||||
self.guildcard_data_header(id).await?.into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
},
|
||||
RecvCharacterPacket::GuildcardDataChunkRequest(request) => {
|
||||
Box::new(self.guildcard_data_chunk(id, request.chunk, request.again)?.into_iter().map(move |pkt| (id, pkt)))
|
||||
self.guildcard_data_chunk(id, request.chunk, request.again).await?.into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
},
|
||||
RecvCharacterPacket::ParamDataRequest(_request) => {
|
||||
Box::new(vec![SendCharacterPacket::ParamDataHeader(self.param_header.clone())].into_iter().map(move |pkt| (id, pkt)))
|
||||
vec![SendCharacterPacket::ParamDataHeader(self.param_header.clone())].into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
},
|
||||
RecvCharacterPacket::SetFlag(flag) => {
|
||||
Box::new(self.set_flag(id, flag).await?.map(move |pkt| (id, pkt)))
|
||||
self.set_flag(id, &flag).await?.map(move |pkt| (id, pkt)).collect()
|
||||
},
|
||||
RecvCharacterPacket::ParamDataChunkRequest(request) => {
|
||||
Box::new(self.param_data_chunk_request(id, request)?.into_iter().map(move |pkt| (id, pkt)))
|
||||
self.param_data_chunk_request(id, &request).await?.into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
},
|
||||
RecvCharacterPacket::CharacterPreview(preview) => {
|
||||
Box::new(self.character_preview(id, preview).await?.into_iter().map(move |pkt| (id, pkt)))
|
||||
self.character_preview(id, &preview).await?.into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
},
|
||||
RecvCharacterPacket::MenuSelect(menuselect) => {
|
||||
Box::new(self.select_ship(id, menuselect)?.into_iter().map(move |pkt| (id, pkt)))
|
||||
self.select_ship(id, &menuselect).await?.into_iter().map(move |pkt| (id, pkt)).collect()
|
||||
},
|
||||
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())
|
||||
SHIP_MENU_ID => self.ship_detail(&menudetail).await?.into_iter().map(move |pkt| (id, pkt)).collect(),
|
||||
_ => Vec::new()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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.write().await.remove(&id) {
|
||||
if let Some(user) = client.user {
|
||||
self.connected_clients.remove(&user.id);
|
||||
self.connected_clients.write().await.remove(&user.id);
|
||||
}
|
||||
}
|
||||
Ok(Vec::new())
|
||||
@ -634,7 +647,7 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<EG: EntityGateway> InterserverActor for CharacterServerState<EG> {
|
||||
impl<EG: EntityGateway + Clone> InterserverActor for CharacterServerState<EG> {
|
||||
type SendMessage = LoginMessage;
|
||||
type RecvMessage = ShipMessage;
|
||||
type Error = ();
|
||||
@ -643,21 +656,26 @@ impl<EG: EntityGateway> InterserverActor for CharacterServerState<EG> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
|
||||
async fn on_action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
|
||||
dbg!(&id, &msg);
|
||||
match msg {
|
||||
ShipMessage::Authenticate(auth_token) => {
|
||||
if self.auth_token == auth_token {
|
||||
self.authenticated_ships.insert(id);
|
||||
self.authenticated_ships.write().await.insert(id);
|
||||
}
|
||||
Ok(Vec::new())
|
||||
},
|
||||
ShipMessage::NewShip(new_ship) => {
|
||||
if self.authenticated_ships.contains(&id) {
|
||||
self.ships.insert(id, new_ship);
|
||||
dbg!("adding ship", &id, &new_ship);
|
||||
if self.authenticated_ships.read().await.contains(&id) {
|
||||
self.ships.write().await.insert(id, new_ship);
|
||||
}
|
||||
dbg!("ship list", &self.authenticated_ships);
|
||||
|
||||
let ships = self.ships.iter().map(|(_, s)| s).cloned().collect::<Vec<_>>();
|
||||
let ships = self.ships.read().await.iter().map(|(_, s)| s).cloned().collect::<Vec<_>>();
|
||||
Ok(self.ships
|
||||
.read()
|
||||
.await
|
||||
.iter()
|
||||
.map(|(id, _)| {
|
||||
(*id, LoginMessage::ShipList{ ships: ships.clone() })
|
||||
@ -665,8 +683,8 @@ impl<EG: EntityGateway> InterserverActor for CharacterServerState<EG> {
|
||||
.collect())
|
||||
},
|
||||
ShipMessage::AddUser(new_user) => {
|
||||
if self.authenticated_ships.contains(&id) {
|
||||
self.connected_clients.insert(new_user, ConnectedClient {
|
||||
if self.authenticated_ships.read().await.contains(&id) {
|
||||
self.connected_clients.write().await.insert(new_user, ConnectedClient {
|
||||
ship_id: Some(id),
|
||||
expires: None,
|
||||
});
|
||||
@ -674,15 +692,18 @@ impl<EG: EntityGateway> InterserverActor for CharacterServerState<EG> {
|
||||
Ok(Vec::new())
|
||||
},
|
||||
ShipMessage::RemoveUser(new_user) => {
|
||||
if self.authenticated_ships.contains(&id) {
|
||||
self.connected_clients.remove(&new_user);
|
||||
if self.authenticated_ships.read().await.contains(&id) {
|
||||
self.connected_clients.write().await.remove(&new_user);
|
||||
}
|
||||
Ok(Vec::new())
|
||||
},
|
||||
ShipMessage::RequestShipList => {
|
||||
if self.authenticated_ships.contains(&id) {
|
||||
dbg!("request ship list", &self.authenticated_ships);
|
||||
if self.authenticated_ships.read().await.contains(&id) {
|
||||
Ok(vec![(id, LoginMessage::ShipList {
|
||||
ships: self.ships
|
||||
.read()
|
||||
.await
|
||||
.iter()
|
||||
.map(|(_, ship)| {
|
||||
ship
|
||||
@ -702,15 +723,20 @@ impl<EG: EntityGateway> InterserverActor for CharacterServerState<EG> {
|
||||
}
|
||||
|
||||
async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
|
||||
self.ships.remove(&id);
|
||||
self.ship_sender.remove(&id);
|
||||
self.connected_clients = self.connected_clients.clone().into_iter()
|
||||
.filter(|(_, client)| {
|
||||
self.ships.write().await.remove(&id);
|
||||
self.ship_sender.write().await.remove(&id);
|
||||
self.connected_clients
|
||||
.write()
|
||||
.await
|
||||
.retain(|_, client| {
|
||||
client.ship_id != Some(id)
|
||||
})
|
||||
.collect();
|
||||
});
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
async fn set_sender(&mut self, server_id: ServerId, sender: channel::Sender<LoginMessage>) {
|
||||
self.ship_sender.write().await.insert(server_id, sender);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -838,11 +864,9 @@ mod test {
|
||||
at_character: false,
|
||||
at_ship: false,
|
||||
});
|
||||
server.clients.insert(ClientId(5), clientstate);
|
||||
server.clients.write().await.insert(ClientId(5), clientstate);
|
||||
|
||||
let send = server.handle(ClientId(5), &RecvCharacterPacket::RequestSettings(RequestSettings{})).await
|
||||
.unwrap()
|
||||
.collect::<Vec<_>>();
|
||||
let send = server.handle(ClientId(5), RecvCharacterPacket::RequestSettings(RequestSettings{})).await.unwrap();
|
||||
assert!(send.len() == 1);
|
||||
|
||||
assert!(send[0].0 == ClientId(5));
|
||||
@ -857,9 +881,9 @@ mod test {
|
||||
struct TestData;
|
||||
impl EntityGateway for 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<_>>();
|
||||
let send = server.handle(ClientId(1), RecvCharacterPacket::Checksum(Checksum {checksum: 1234,
|
||||
padding: 0,
|
||||
})).await.unwrap();
|
||||
assert!(send.len() == 1);
|
||||
|
||||
let bytes = send[0].1.as_bytes();
|
||||
@ -887,10 +911,10 @@ mod test {
|
||||
});
|
||||
|
||||
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<_>>();
|
||||
server.clients.write().await.insert(ClientId(1), fake_user.clone());
|
||||
let mut send = server.handle(ClientId(1), RecvCharacterPacket::SetFlag(SetFlag {flags: 1})).await.unwrap();
|
||||
assert!(test_data.get_user_by_id(UserAccountId(3)).await.unwrap().flags == 1);
|
||||
send = server.handle(ClientId(1), &RecvCharacterPacket::CharacterPreview(CharacterPreview {slot: 1, character: character::SelectScreenCharacter {
|
||||
send = server.handle(ClientId(1), RecvCharacterPacket::CharacterPreview(CharacterPreview {slot: 1, character: character::SelectScreenCharacter {
|
||||
exp: 0,
|
||||
level: 0,
|
||||
guildcard: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
|
||||
@ -916,7 +940,7 @@ mod test {
|
||||
prop_y: 0.0,
|
||||
name: [9, 69, 116, 101, 115, 116, 32, 110, 97, 109, 101, 0, 0, 0, 0, 0], // "\tEtest name"
|
||||
play_time: 0,
|
||||
} })).await.unwrap().collect::<Vec<_>>();
|
||||
} })).await.unwrap();
|
||||
assert!(send.len() == 2);
|
||||
|
||||
let chars = test_data.get_characters_by_user(&fake_user.user.unwrap()).await.unwrap();
|
||||
|
@ -93,13 +93,14 @@ pub fn check_if_already_online(user: UserAccountEntity) -> Result<UserAccountEnt
|
||||
*/
|
||||
}
|
||||
|
||||
pub struct LoginServerState<EG: EntityGateway> {
|
||||
#[derive(Clone)]
|
||||
pub struct LoginServerState<EG: EntityGateway + Clone> {
|
||||
character_server_ip: net::Ipv4Addr,
|
||||
entity_gateway: EG,
|
||||
clients: HashMap<ClientId, String>,
|
||||
}
|
||||
|
||||
impl<EG: EntityGateway> LoginServerState<EG> {
|
||||
impl<EG: EntityGateway + Clone> LoginServerState<EG> {
|
||||
pub fn new(entity_gateway: EG, character_server_ip: net::Ipv4Addr) -> LoginServerState<EG> {
|
||||
LoginServerState {
|
||||
entity_gateway,
|
||||
@ -128,13 +129,14 @@ impl<EG: EntityGateway> LoginServerState<EG> {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<EG: EntityGateway> ServerState for LoginServerState<EG> {
|
||||
impl<EG: EntityGateway + Clone> ServerState for LoginServerState<EG> {
|
||||
type SendPacket = SendLoginPacket;
|
||||
type RecvPacket = RecvLoginPacket;
|
||||
type Cipher = PSOBBCipher;
|
||||
//type PacketError = LoginError;
|
||||
type PacketError = anyhow::Error;
|
||||
|
||||
async fn on_connect(&mut self, _id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, anyhow::Error> {
|
||||
async fn on_connect(&mut self, _id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket, Self::Cipher>>, anyhow::Error> {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let mut server_key = [0u8; 48];
|
||||
@ -143,20 +145,20 @@ impl<EG: EntityGateway> ServerState for LoginServerState<EG> {
|
||||
rng.fill(&mut client_key[..]);
|
||||
|
||||
Ok(vec![OnConnect::Packet(SendLoginPacket::LoginWelcome(LoginWelcome::new(server_key, client_key))),
|
||||
OnConnect::Cipher((Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key)),
|
||||
Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key))))
|
||||
OnConnect::Cipher(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key),
|
||||
PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key))
|
||||
])
|
||||
}
|
||||
|
||||
async fn handle(&mut self, id: ClientId, pkt: &Self::RecvPacket)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, Self::SendPacket)> + Send>, anyhow::Error> {
|
||||
async fn handle(&mut self, id: ClientId, pkt: Self::RecvPacket) -> Result<Vec<(ClientId, Self::SendPacket)>, anyhow::Error> {
|
||||
Ok(match pkt {
|
||||
RecvLoginPacket::Login(login) => {
|
||||
Box::new(self.validate_login(id, login).await?
|
||||
.into_iter()
|
||||
.map(move |pkt| {
|
||||
(id, pkt)
|
||||
}))
|
||||
self.validate_login(id, &login).await?
|
||||
.into_iter()
|
||||
.map(move |pkt| {
|
||||
(id, pkt)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -237,7 +239,7 @@ mod test {
|
||||
|
||||
let mut server = LoginServerState::new(TestData {}, "127.0.0.1".parse().unwrap());
|
||||
|
||||
let send = server.handle(ClientId(1), &LOGIN_PACKET).await.unwrap().collect::<Vec<_>>();
|
||||
let send = server.handle(ClientId(1), LOGIN_PACKET).await.unwrap();
|
||||
assert!(send == vec![
|
||||
(ClientId(1), SendLoginPacket::LoginResponse(LoginResponse {
|
||||
status: AccountStatus::Ok,
|
||||
@ -275,7 +277,7 @@ mod test {
|
||||
}
|
||||
|
||||
let mut server = LoginServerState::new(TestData {}, "127.0.0.1".parse().unwrap());
|
||||
let send = server.handle(ClientId(1), &LOGIN_PACKET).await.unwrap().collect::<Vec<_>>();
|
||||
let send = server.handle(ClientId(1), LOGIN_PACKET).await.unwrap();
|
||||
|
||||
assert!(send == vec![
|
||||
(ClientId(1), SendLoginPacket::LoginResponse(LoginResponse {
|
||||
@ -324,7 +326,7 @@ mod test {
|
||||
}
|
||||
|
||||
let mut server = LoginServerState::new(TestData {}, "127.0.0.1".parse().unwrap());
|
||||
let send = server.handle(ClientId(1), &LOGIN_PACKET).await.unwrap().collect::<Vec<_>>();
|
||||
let send = server.handle(ClientId(1), LOGIN_PACKET).await.unwrap();
|
||||
|
||||
assert!(send == vec![
|
||||
(ClientId(1), SendLoginPacket::LoginResponse(LoginResponse {
|
||||
@ -373,7 +375,7 @@ mod test {
|
||||
}
|
||||
|
||||
let mut server = LoginServerState::new(TestData {}, "127.0.0.1".parse().unwrap());
|
||||
let send = server.handle(ClientId(1), &LOGIN_PACKET).await.unwrap().collect::<Vec<_>>();
|
||||
let send = server.handle(ClientId(1), LOGIN_PACKET).await.unwrap();
|
||||
|
||||
assert!(send == vec![
|
||||
(ClientId(1), SendLoginPacket::LoginResponse(LoginResponse {
|
||||
|
@ -136,6 +136,7 @@ impl SendServerPacket for SendPatchPacket {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PatchServerState {
|
||||
patch_file_tree: PatchFileTree,
|
||||
patch_file_lookup: HashMap<u32, PatchFile>,
|
||||
@ -158,33 +159,39 @@ impl PatchServerState {
|
||||
impl ServerState for PatchServerState {
|
||||
type SendPacket = SendPatchPacket;
|
||||
type RecvPacket = RecvPatchPacket;
|
||||
type Cipher = PSOPCCipher;
|
||||
type PacketError = PatchError;
|
||||
|
||||
async fn on_connect(&mut self, _id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, PatchError> {
|
||||
async fn on_connect(&mut self, _id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket, Self::Cipher>>, PatchError> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let key_in: u32 = rng.gen();
|
||||
let key_out: u32 = rng.gen();
|
||||
|
||||
Ok(vec![OnConnect::Packet(SendPatchPacket::PatchWelcome(PatchWelcome::new(key_out, key_in))),
|
||||
OnConnect::Cipher((Box::new(PSOPCCipher::new(key_in)), Box::new(PSOPCCipher::new(key_out))))
|
||||
OnConnect::Cipher(PSOPCCipher::new(key_in), PSOPCCipher::new(key_out))
|
||||
])
|
||||
}
|
||||
|
||||
async fn handle(&mut self, id: ClientId, pkt: &RecvPatchPacket)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendPatchPacket)> + Send>, PatchError> {
|
||||
async fn handle(&mut self, id: ClientId, pkt: RecvPatchPacket) -> Result<Vec<(ClientId, SendPatchPacket)>, PatchError> {
|
||||
Ok(match pkt {
|
||||
RecvPatchPacket::PatchWelcomeReply(_pkt) => {
|
||||
Box::new(vec![SendPatchPacket::RequestLogin(RequestLogin {})].into_iter().map(move |pkt| (id, pkt)))
|
||||
vec![SendPatchPacket::RequestLogin(RequestLogin {})]
|
||||
.into_iter()
|
||||
.map(move |pkt| (id, pkt))
|
||||
.collect()
|
||||
},
|
||||
RecvPatchPacket::LoginReply(_pkt) => {
|
||||
let mut p = vec![SendPatchPacket::Message(Message::new(self.patch_motd.clone()))];
|
||||
p.append(&mut get_file_list_packets(&self.patch_file_tree));
|
||||
p.push(SendPatchPacket::PatchEndList(PatchEndList {}));
|
||||
Box::new(p.into_iter().map(move |pkt| (id, pkt)))
|
||||
let mut pkts = vec![SendPatchPacket::Message(Message::new(self.patch_motd.clone()))];
|
||||
pkts.append(&mut get_file_list_packets(&self.patch_file_tree));
|
||||
pkts.push(SendPatchPacket::PatchEndList(PatchEndList {}));
|
||||
pkts
|
||||
.into_iter()
|
||||
.map(move |pkt| (id, pkt))
|
||||
.collect()
|
||||
},
|
||||
RecvPatchPacket::FileInfoReply(pkt) => {
|
||||
self.patch_file_info.push(pkt.clone());
|
||||
Box::new(None.into_iter().map(move |pkt| (id, pkt)))
|
||||
self.patch_file_info.push(pkt);
|
||||
Vec::new()
|
||||
},
|
||||
RecvPatchPacket::FileInfoListEnd(_pkt) => {
|
||||
let need_update = self.patch_file_info.iter()
|
||||
@ -194,10 +201,12 @@ impl ServerState for PatchServerState {
|
||||
let total_size = need_update.iter().fold(0, |a, file_info| a + file_info.size);
|
||||
let total_files = need_update.len() as u32;
|
||||
|
||||
let p = vec![SendPatchPacket::FilesToPatchMetadata(FilesToPatchMetadata::new(total_size, total_files)),
|
||||
SendPatchPacket::PatchStartList(PatchStartList {})
|
||||
];
|
||||
Box::new(p.into_iter().chain(SendFileIterator::new(self)).map(move |pkt| (id, pkt)))
|
||||
vec![SendPatchPacket::FilesToPatchMetadata(FilesToPatchMetadata::new(total_size, total_files)),
|
||||
SendPatchPacket::PatchStartList(PatchStartList {})]
|
||||
.into_iter()
|
||||
.chain(SendFileIterator::new(self))
|
||||
.map(move |pkt| (id, pkt))
|
||||
.collect()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ impl<'a> CharacterBytesBuilder<'a> {
|
||||
prop_y: character.appearance.prop_y,
|
||||
config: character.config.as_bytes(),
|
||||
techniques: character.techs.as_bytes(),
|
||||
meseta: meseta.0 as u32,
|
||||
meseta: meseta.0,
|
||||
exp: character.exp,
|
||||
..character::Character::default()
|
||||
}
|
||||
|
172
src/ship/client.rs
Normal file
172
src/ship/client.rs
Normal file
@ -0,0 +1,172 @@
|
||||
use std::collections::HashMap;
|
||||
use async_std::sync::{Arc, RwLock, RwLockReadGuard};
|
||||
|
||||
use futures::future::BoxFuture;
|
||||
|
||||
use libpso::packet::ship::*;
|
||||
use libpso::packet::login::Session;
|
||||
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::entity::account::{UserAccountEntity, UserSettingsEntity};
|
||||
use crate::entity::character::CharacterEntity;
|
||||
use crate::entity::item;
|
||||
|
||||
use crate::ship::ship::ShipError;
|
||||
use crate::ship::items;
|
||||
use crate::ship::map::MapArea;
|
||||
use crate::ship::shops::{WeaponShopItem, ToolShopItem, ArmorShopItem};
|
||||
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Clients(Arc<RwLock<HashMap<ClientId, RwLock<ClientState>>>>);
|
||||
|
||||
impl Clients {
|
||||
pub async fn add(&mut self, client_id: ClientId, client_state: ClientState) {
|
||||
self.0
|
||||
.write()
|
||||
.await
|
||||
.insert(client_id, RwLock::new(client_state));
|
||||
}
|
||||
|
||||
pub async fn remove(&mut self, client_id: &ClientId) -> Option<ClientState> {
|
||||
Some(self.0
|
||||
.write()
|
||||
.await
|
||||
.remove(client_id)?
|
||||
.into_inner())
|
||||
}
|
||||
|
||||
pub async fn with<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, ShipError>
|
||||
where
|
||||
T: Send,
|
||||
F: for<'b> FnOnce(&'b ClientState) -> BoxFuture<'b, T> + Send + 'a,
|
||||
{
|
||||
let clients = self.0
|
||||
.read()
|
||||
.await;
|
||||
let client = clients
|
||||
.get(&client_id)
|
||||
.ok_or_else(|| ShipError::ClientNotFound(client_id))?
|
||||
.read()
|
||||
.await;
|
||||
|
||||
Ok(func(&client).await)
|
||||
}
|
||||
|
||||
pub async fn with_many<'a, T, F, const N: usize>(&'a self, client_ids: [ClientId; N], func: F) -> Result<T, ShipError>
|
||||
where
|
||||
T: Send,
|
||||
F: for<'b> FnOnce([RwLockReadGuard<'b, ClientState>; N]) -> BoxFuture<'b, T> + Send + 'a,
|
||||
{
|
||||
let clients = self.0
|
||||
.read()
|
||||
.await;
|
||||
|
||||
let mut client_states: [std::mem::MaybeUninit<RwLockReadGuard<ClientState>>; N] = unsafe {
|
||||
std::mem::MaybeUninit::uninit().assume_init()
|
||||
};
|
||||
|
||||
for (cindex, client_id) in client_ids.iter().enumerate() {
|
||||
let c = clients
|
||||
.get(client_id)
|
||||
.ok_or_else(|| ShipError::ClientNotFound(*client_id))?
|
||||
.read()
|
||||
.await;
|
||||
client_states[cindex].write(c);
|
||||
}
|
||||
|
||||
let client_states = unsafe {
|
||||
// TODO: this should just be a normal transmute but due to compiler limitations it
|
||||
// does not yet work with const generics
|
||||
// https://github.com/rust-lang/rust/issues/61956
|
||||
std::mem::transmute_copy::<_, [RwLockReadGuard<ClientState>; N]>(&client_states)
|
||||
};
|
||||
|
||||
Ok(func(client_states).await)
|
||||
}
|
||||
|
||||
pub async fn with_mut<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, ShipError>
|
||||
where
|
||||
T: Send,
|
||||
F: for<'b> FnOnce(&'b mut ClientState) -> BoxFuture<'b, T> + Send + 'a,
|
||||
{
|
||||
let clients = self.0
|
||||
.read()
|
||||
.await;
|
||||
let mut client = clients
|
||||
.get(&client_id)
|
||||
.ok_or_else(|| ShipError::ClientNotFound(client_id))?
|
||||
.write()
|
||||
.await;
|
||||
|
||||
Ok(func(&mut client).await)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ItemDropLocation {
|
||||
pub map_area: MapArea,
|
||||
pub x: f32,
|
||||
pub z: f32,
|
||||
pub item_id: items::ClientItemId,
|
||||
}
|
||||
|
||||
pub struct LoadingQuest {
|
||||
pub header_bin: Option<QuestHeader>,
|
||||
pub header_dat: Option<QuestHeader>,
|
||||
}
|
||||
|
||||
|
||||
pub struct ClientState {
|
||||
pub user: UserAccountEntity,
|
||||
pub settings: UserSettingsEntity,
|
||||
pub character: CharacterEntity,
|
||||
_session: Session,
|
||||
//guildcard: GuildCard,
|
||||
pub block: usize,
|
||||
pub item_drop_location: Option<ItemDropLocation>,
|
||||
pub done_loading_quest: bool,
|
||||
pub area: Option<MapArea>,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
pub weapon_shop: Vec<WeaponShopItem>,
|
||||
pub tool_shop: Vec<ToolShopItem>,
|
||||
pub armor_shop: Vec<ArmorShopItem>,
|
||||
pub tek: Option<(items::ClientItemId, item::weapon::TekSpecialModifier, item::weapon::TekPercentModifier, i32)>,
|
||||
pub character_playtime: chrono::Duration,
|
||||
pub log_on_time: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
impl ClientState {
|
||||
pub fn new(user: UserAccountEntity, settings: UserSettingsEntity, character: CharacterEntity, session: Session) -> ClientState {
|
||||
let character_playtime = chrono::Duration::seconds(character.playtime as i64);
|
||||
ClientState {
|
||||
user,
|
||||
settings,
|
||||
character,
|
||||
_session: session,
|
||||
block: 0,
|
||||
item_drop_location: None,
|
||||
done_loading_quest: false,
|
||||
area: None,
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
weapon_shop: Vec::new(),
|
||||
tool_shop: Vec::new(),
|
||||
armor_shop: Vec::new(),
|
||||
tek: None,
|
||||
character_playtime,
|
||||
log_on_time: chrono::Utc::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_playtime(&mut self) {
|
||||
let additional_playtime = chrono::Utc::now() - self.log_on_time;
|
||||
self.character.playtime = (self.character_playtime + additional_playtime).num_seconds() as u32;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,9 +29,9 @@ pub(super) fn take_item_from_floor(character_id: CharacterEntityId, item_id: Cli
|
||||
{
|
||||
move |(mut item_state, transaction): (ItemStateProxy<'_>, Box<dyn EntityGatewayTransaction + '_>) , _| {
|
||||
Box::pin(async move {
|
||||
let mut floor = item_state.floor(&character_id)?;
|
||||
let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?;
|
||||
item_state.set_floor(floor);
|
||||
let mut floor = item_state.floor(&character_id).await?;
|
||||
let item = floor.take_item(&item_id).ok_or_else(|| ItemStateError::NoFloorItem(item_id))?;
|
||||
item_state.set_floor(floor).await;
|
||||
|
||||
Ok(((item_state, transaction), item))
|
||||
})
|
||||
@ -46,7 +46,7 @@ pub(super) fn add_floor_item_to_inventory(character: &CharacterEntity)
|
||||
move |(mut item_state, transaction), floor_item| {
|
||||
let character = character.clone();
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character.id)?;
|
||||
let mut inventory = item_state.inventory(&character.id).await?;
|
||||
|
||||
let character_id = character.id;
|
||||
let transaction = floor_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
@ -87,8 +87,8 @@ pub(super) fn take_item_from_inventory(character_id: CharacterEntityId, item_id:
|
||||
{
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character_id)?;
|
||||
let item = inventory.take_item(&item_id, amount).ok_or (ItemStateError::NoFloorItem(item_id))?;
|
||||
let mut inventory = item_state.inventory(&character_id).await?;
|
||||
let item = inventory.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoFloorItem(item_id))?;
|
||||
|
||||
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
|
||||
item_state.set_inventory(inventory);
|
||||
@ -117,9 +117,9 @@ pub(super) fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId
|
||||
Ok(transaction)
|
||||
}}).await?;
|
||||
|
||||
let mut floor = item_state.floor(&character_id)?;
|
||||
let mut floor = item_state.floor(&character_id).await?;
|
||||
let floor_item = floor.add_inventory_item(inventory_item, map_area, drop_position).clone();
|
||||
item_state.set_floor(floor);
|
||||
item_state.set_floor(floor).await;
|
||||
|
||||
Ok(((item_state, transaction), floor_item))
|
||||
})
|
||||
@ -133,7 +133,7 @@ pub(super) fn take_meseta_from_inventory(character_id: CharacterEntityId, amount
|
||||
{
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character_id)?;
|
||||
let mut inventory = item_state.inventory(&character_id).await?;
|
||||
inventory.remove_meseta(amount)?;
|
||||
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||
item_state.set_inventory(inventory);
|
||||
@ -149,7 +149,7 @@ pub(super) fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u
|
||||
{
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character_id)?;
|
||||
let mut inventory = item_state.inventory(&character_id).await?;
|
||||
inventory.add_meseta(amount)?;
|
||||
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||
item_state.set_inventory(inventory);
|
||||
@ -167,7 +167,7 @@ pub(super) fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount
|
||||
move |(mut item_state, transaction), _| {
|
||||
Box::pin(async move {
|
||||
let floor_item = FloorItem {
|
||||
item_id: item_state.new_item_id()?,
|
||||
item_id: item_state.new_item_id().await?,
|
||||
item: FloorItemDetail::Meseta(Meseta(amount)),
|
||||
map_area,
|
||||
x: drop_position.0,
|
||||
@ -175,9 +175,9 @@ pub(super) fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount
|
||||
z: drop_position.1,
|
||||
};
|
||||
|
||||
let mut floor = item_state.floor(&character_id)?;
|
||||
let mut floor = item_state.floor(&character_id).await?;
|
||||
let floor_item = floor.add_shared_item(floor_item).clone();
|
||||
item_state.set_floor(floor);
|
||||
item_state.set_floor(floor).await;
|
||||
|
||||
Ok(((item_state, transaction), floor_item))
|
||||
})
|
||||
@ -190,7 +190,7 @@ pub(super) fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32
|
||||
{
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
Box::pin(async move {
|
||||
let mut bank = item_state.bank(&character_id)?;
|
||||
let mut bank = item_state.bank(&character_id).await?;
|
||||
bank.remove_meseta(amount)?;
|
||||
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
|
||||
|
||||
@ -205,7 +205,7 @@ pub(super) fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId,
|
||||
{
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character_id)?;
|
||||
let mut inventory = item_state.inventory(&character_id).await?;
|
||||
inventory.add_meseta_no_overflow(amount)?;
|
||||
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
|
||||
|
||||
@ -221,7 +221,7 @@ pub(super) fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32)
|
||||
{
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
Box::pin(async move {
|
||||
let mut bank = item_state.bank(&character_id)?;
|
||||
let mut bank = item_state.bank(&character_id).await?;
|
||||
bank.add_meseta(amount)?;
|
||||
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
|
||||
|
||||
@ -237,8 +237,8 @@ pub(super) fn take_item_from_bank(character_id: CharacterEntityId, item_id: Clie
|
||||
{
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
Box::pin(async move {
|
||||
let mut bank = item_state.bank(&character_id)?;
|
||||
let item = bank.take_item(&item_id, amount).ok_or(ItemStateError::NoBankItem(item_id))?;
|
||||
let mut bank = item_state.bank(&character_id).await?;
|
||||
let item = bank.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoBankItem(item_id))?;
|
||||
transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?;
|
||||
item_state.set_bank(bank);
|
||||
|
||||
@ -255,8 +255,8 @@ pub(super) fn add_bank_item_to_inventory(character: &CharacterEntity)
|
||||
move |(mut item_state, transaction), bank_item| {
|
||||
let character = character.clone();
|
||||
Box::pin(async move {
|
||||
let bank_name = item_state.bank(&character.id)?.name;
|
||||
let mut inventory = item_state.inventory(&character.id)?;
|
||||
let bank_name = item_state.bank(&character.id).await?.name;
|
||||
let mut inventory = item_state.inventory(&character.id).await?;
|
||||
|
||||
let character_id = character.id;
|
||||
let transaction = bank_item.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
@ -300,7 +300,7 @@ pub(super) fn add_inventory_item_to_bank(character_id: CharacterEntityId)
|
||||
{
|
||||
move |(mut item_state, transaction), inventory_item| {
|
||||
Box::pin(async move {
|
||||
let mut bank = item_state.bank(&character_id)?;
|
||||
let mut bank = item_state.bank(&character_id).await?;
|
||||
let bank_name = bank.name.clone();
|
||||
let mut transaction = inventory_item.with_entity_id(transaction, move |mut transaction, entity_id| {
|
||||
let bank_name = bank_name.clone();
|
||||
@ -330,7 +330,7 @@ pub(super) fn equip_inventory_item(character_id: CharacterEntityId, item_id: Cli
|
||||
{
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character_id)?;
|
||||
let mut inventory = item_state.inventory(&character_id).await?;
|
||||
inventory.equip(&item_id, equip_slot);
|
||||
transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?;
|
||||
item_state.set_inventory(inventory);
|
||||
@ -347,7 +347,7 @@ pub(super) fn unequip_inventory_item(character_id: CharacterEntityId, item_id: C
|
||||
{
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character_id)?;
|
||||
let mut inventory = item_state.inventory(&character_id).await?;
|
||||
inventory.unequip(&item_id);
|
||||
transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?;
|
||||
item_state.set_inventory(inventory);
|
||||
@ -366,7 +366,7 @@ pub(super) fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Ve
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
let item_ids = item_ids.clone();
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character_id)?;
|
||||
let mut inventory = item_state.inventory(&character_id).await?;
|
||||
inventory.sort(&item_ids);
|
||||
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
|
||||
item_state.set_inventory(inventory);
|
||||
@ -405,12 +405,12 @@ pub(super) fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemI
|
||||
move |(mut item_state, transaction), tool| {
|
||||
let character = character.clone();
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character.id)?;
|
||||
let mut inventory = item_state.inventory(&character.id).await?;
|
||||
let mag_entity = inventory.get_by_client_id_mut(&mag_item_id)
|
||||
.ok_or(ItemStateError::InvalidItemId(mag_item_id))?
|
||||
.ok_or_else(|| ItemStateError::InvalidItemId(mag_item_id))?
|
||||
.item
|
||||
.as_individual_mut()
|
||||
.ok_or(ItemStateError::NotAMag(mag_item_id))?;
|
||||
.ok_or_else(|| ItemStateError::NotAMag(mag_item_id))?;
|
||||
let mag_entity_id = mag_entity.entity_id;
|
||||
|
||||
let mut transaction = tool.with_entity_id(transaction, |mut transaction, entity_id| {
|
||||
@ -426,13 +426,13 @@ pub(super) fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemI
|
||||
let food_tool = tool
|
||||
.item
|
||||
.stacked()
|
||||
.ok_or(ItemStateError::NotMagFood(tool.item_id))?
|
||||
.ok_or_else(|| ItemStateError::NotMagFood(tool.item_id))?
|
||||
.tool
|
||||
.tool;
|
||||
|
||||
let mag_entity = mag_entity
|
||||
.as_mag_mut()
|
||||
.ok_or(ItemStateError::NotAMag(mag_item_id))?;
|
||||
.ok_or_else(|| ItemStateError::NotAMag(mag_item_id))?;
|
||||
|
||||
mag_entity.feed(food_tool);
|
||||
|
||||
@ -454,7 +454,7 @@ pub(super) fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId,
|
||||
{
|
||||
move |(mut item_state, mut transaction), _| {
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character_id)?;
|
||||
let mut inventory = item_state.inventory(&character_id).await?;
|
||||
let bought_item = shop_item.as_item();
|
||||
|
||||
let inventory_item = match bought_item {
|
||||
@ -512,7 +512,7 @@ pub(super) fn sell_inventory_item<'a>(character_id: CharacterEntityId)
|
||||
{
|
||||
move |(mut item_state, transaction), inventory_item| {
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character_id)?;
|
||||
let mut inventory = item_state.inventory(&character_id).await?;
|
||||
let price = inventory_item.item.sell_price()?;
|
||||
inventory.add_meseta_no_overflow(price)?;
|
||||
|
||||
@ -575,7 +575,6 @@ where
|
||||
move |(item_state, transaction), arg| {
|
||||
let input = input.clone();
|
||||
let func = func.clone();
|
||||
println!("i {:?} {:?}", input, arg);
|
||||
Box::pin(async move {
|
||||
let (state, result) = iterate_inner((item_state, transaction), input, func, arg.clone()).await?;
|
||||
Ok((state, result))
|
||||
@ -622,7 +621,6 @@ where
|
||||
T: Clone + Send + Sync,
|
||||
{
|
||||
move |(item_state, transaction), items| {
|
||||
println!("fe {:?}", items);
|
||||
let func = func.clone();
|
||||
Box::pin(async move {
|
||||
let (state, result) = foreach_inner((item_state, transaction), items, func).await?;
|
||||
@ -650,7 +648,7 @@ pub(super) fn add_item_to_inventory(character: CharacterEntity)
|
||||
move |(mut item_state, transaction), inventory_item| {
|
||||
let character = character.clone();
|
||||
Box::pin(async move {
|
||||
let mut inventory = item_state.inventory(&character.id)?;
|
||||
let mut inventory = item_state.inventory(&character.id).await?;
|
||||
let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
|
||||
let character = character.clone();
|
||||
async move {
|
||||
@ -696,7 +694,7 @@ pub(super) fn assign_new_item_id()
|
||||
{
|
||||
move |(mut item_state, transaction), mut inventory_item| {
|
||||
Box::pin(async move {
|
||||
inventory_item.item_id = item_state.new_item_id()?;
|
||||
inventory_item.item_id = item_state.new_item_id().await?;
|
||||
Ok(((item_state, transaction), inventory_item))
|
||||
})
|
||||
}
|
||||
@ -734,7 +732,7 @@ pub(super) fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, i
|
||||
ItemDropType::Meseta(m) => ItemOrMeseta::Meseta(Meseta(m)),
|
||||
};
|
||||
|
||||
let item_id = item_state.new_item_id()?;
|
||||
let item_id = item_state.new_item_id().await?;
|
||||
|
||||
let floor_item = match item {
|
||||
ItemOrMeseta::Individual(item_detail) => {
|
||||
@ -806,9 +804,9 @@ pub(super) fn add_item_to_local_floor(character_id: CharacterEntityId)
|
||||
{
|
||||
move |(mut item_state, transaction) , floor_item| {
|
||||
Box::pin(async move {
|
||||
let mut floor = item_state.floor(&character_id)?;
|
||||
let mut floor = item_state.floor(&character_id).await?;
|
||||
let item = floor.add_local_item(floor_item).clone();
|
||||
item_state.set_floor(floor);
|
||||
item_state.set_floor(floor).await;
|
||||
|
||||
Ok(((item_state, transaction), item))
|
||||
})
|
||||
|
@ -107,7 +107,7 @@ async fn mag_cell<'a, EG>(item_state: &mut ItemStateProxy<'a>,
|
||||
where
|
||||
EG: EntityGateway + ?Sized,
|
||||
{
|
||||
let mut inventory = item_state.inventory(&character.id)?;
|
||||
let mut inventory = item_state.inventory(&character.id).await?;
|
||||
|
||||
let (mag_entity_id, mag) = inventory.equipped_mag_mut()
|
||||
.ok_or(ApplyItemError::ItemNotEquipped)?;
|
||||
|
@ -1,18 +1,19 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::ship::items::ClientItemId;
|
||||
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankName};
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
use futures::future::join_all;
|
||||
|
||||
use crate::ship::location::{AreaClient, RoomId};
|
||||
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankName};
|
||||
use crate::entity::item::tool::Tool;
|
||||
use crate::entity::item::weapon::Weapon;
|
||||
use crate::entity::item::mag::Mag;
|
||||
use crate::ship::drops::ItemDrop;
|
||||
|
||||
use crate::ship::items::ClientItemId;
|
||||
use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState};
|
||||
use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType};
|
||||
use crate::ship::items::bank::{Bank, BankState, BankItem, BankItemDetail, BankError};
|
||||
use crate::ship::location::{AreaClient, RoomId};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ItemStateError {
|
||||
@ -22,55 +23,38 @@ pub enum ItemStateError {
|
||||
NoRoom(RoomId),
|
||||
#[error("floor item {0} not found")]
|
||||
NoFloorItem(ClientItemId),
|
||||
|
||||
#[error("expected {0} to be a tool")]
|
||||
NotATool(ClientItemId),
|
||||
|
||||
#[error("bank item {0} not found")]
|
||||
NoBankItem(ClientItemId),
|
||||
|
||||
#[error("inventory error {0}")]
|
||||
InventoryError(#[from] InventoryError),
|
||||
|
||||
#[error("bank error {0}")]
|
||||
BankError(#[from] BankError),
|
||||
|
||||
#[error("invalid item id {0}")]
|
||||
InvalidItemId(ClientItemId),
|
||||
|
||||
#[error("invalid drop? {0:?} (this shouldn't occur)")]
|
||||
BadItemDrop(ItemDrop),
|
||||
|
||||
#[error("idk")]
|
||||
Dummy,
|
||||
|
||||
#[error("gateway")]
|
||||
GatewayError(#[from] GatewayError),
|
||||
|
||||
#[error("tried to remove more meseta than exists: {0}")]
|
||||
InvalidMesetaRemoval(u32),
|
||||
|
||||
#[error("tried to add meseta when there is no more room")]
|
||||
FullOfMeseta,
|
||||
|
||||
#[error("stacked item")]
|
||||
StackedItemError(Vec<ItemEntity>),
|
||||
|
||||
#[error("apply item {0}")]
|
||||
ApplyItemError(#[from] crate::ship::items::apply_item::ApplyItemError),
|
||||
|
||||
#[error("item is not a mag {0}")]
|
||||
NotAMag(ClientItemId),
|
||||
|
||||
#[error("item is not mag food {0}")]
|
||||
NotMagFood(ClientItemId),
|
||||
|
||||
#[error("item is not sellable")]
|
||||
ItemNotSellable,
|
||||
|
||||
#[error("could not modify item")]
|
||||
InvalidModifier,
|
||||
|
||||
#[error("wrong item type ")]
|
||||
WrongItemType(ClientItemId),
|
||||
}
|
||||
@ -132,7 +116,6 @@ impl StackedItemDetail {
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AddItemResult {
|
||||
NewItem,
|
||||
@ -141,46 +124,61 @@ pub enum AddItemResult {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ItemState {
|
||||
character_inventory: HashMap<CharacterEntityId, InventoryState>,
|
||||
character_bank: HashMap<CharacterEntityId, BankState>,
|
||||
character_inventory: Arc<RwLock<HashMap<CharacterEntityId, RwLock<InventoryState>>>>,
|
||||
character_bank: Arc<RwLock<HashMap<CharacterEntityId, RwLock<BankState>>>>,
|
||||
|
||||
character_room: HashMap<CharacterEntityId, RoomId>,
|
||||
character_floor: HashMap<CharacterEntityId, LocalFloor>,
|
||||
room_floor: HashMap<RoomId, SharedFloor>,
|
||||
character_room: Arc<RwLock<HashMap<CharacterEntityId, RoomId>>>,
|
||||
character_floor: Arc<RwLock<HashMap<CharacterEntityId, RwLock<LocalFloor>>>>,
|
||||
room_floor: Arc<RwLock<HashMap<RoomId, RwLock<SharedFloor>>>>,
|
||||
|
||||
room_item_id_counter: u32,
|
||||
room_item_id_counter: Arc<RwLock<u32>>,
|
||||
}
|
||||
|
||||
impl Default for ItemState {
|
||||
fn default() -> ItemState {
|
||||
ItemState {
|
||||
character_inventory: HashMap::new(),
|
||||
character_bank: HashMap::new(),
|
||||
character_room: HashMap::new(),
|
||||
character_floor: HashMap::new(),
|
||||
room_floor: HashMap::new(),
|
||||
room_item_id_counter: 0x00810000,
|
||||
character_inventory: Arc::new(RwLock::new(HashMap::new())),
|
||||
character_bank: Arc::new(RwLock::new(HashMap::new())),
|
||||
character_room: Arc::new(RwLock::new(HashMap::new())),
|
||||
character_floor: Arc::new(RwLock::new(HashMap::new())),
|
||||
room_floor: Arc::new(RwLock::new(HashMap::new())),
|
||||
room_item_id_counter: Arc::new(RwLock::new(0x00810000)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemState {
|
||||
pub fn get_character_inventory(&self, character: &CharacterEntity) -> Result<&InventoryState, ItemStateError> {
|
||||
self.character_inventory.get(&character.id)
|
||||
.ok_or(ItemStateError::NoCharacter(character.id))
|
||||
pub async fn get_character_inventory(&self, character: &CharacterEntity) -> Result<InventoryState, ItemStateError> {
|
||||
Ok(self.character_inventory
|
||||
.read()
|
||||
.await
|
||||
.get(&character.id)
|
||||
.ok_or_else(|| ItemStateError::NoCharacter(character.id))?
|
||||
.read()
|
||||
.await
|
||||
.clone())
|
||||
}
|
||||
|
||||
pub fn get_character_bank(&self, character: &CharacterEntity) -> Result<&BankState, ItemStateError> {
|
||||
self.character_bank.get(&character.id)
|
||||
.ok_or(ItemStateError::NoCharacter(character.id))
|
||||
pub async fn get_character_bank(&self, character: &CharacterEntity) -> Result<BankState, ItemStateError> {
|
||||
Ok(self.character_bank
|
||||
.read()
|
||||
.await
|
||||
.get(&character.id)
|
||||
.ok_or_else(|| ItemStateError::NoCharacter(character.id))?
|
||||
.read()
|
||||
.await
|
||||
.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemState {
|
||||
fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> {
|
||||
self.room_item_id_counter += 1;
|
||||
Ok(ClientItemId(self.room_item_id_counter))
|
||||
async fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> {
|
||||
*self.room_item_id_counter
|
||||
.write()
|
||||
.await += 1;
|
||||
Ok(ClientItemId(*self.room_item_id_counter.read().await))
|
||||
}
|
||||
|
||||
pub async fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), ItemStateError> {
|
||||
@ -216,7 +214,7 @@ impl ItemState {
|
||||
},
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
.collect::<Result<Vec<_>, ItemStateError>>()?;
|
||||
|
||||
let character_meseta = entity_gateway.get_character_meseta(&character.id).await?;
|
||||
let inventory_state = InventoryState {
|
||||
@ -226,85 +224,151 @@ impl ItemState {
|
||||
equipped,
|
||||
meseta: character_meseta,
|
||||
};
|
||||
|
||||
let bank_items = bank.items.into_iter()
|
||||
.map(|item| -> Result<BankItem, ItemStateError> {
|
||||
Ok(match item {
|
||||
BankItemEntity::Individual(item) => {
|
||||
BankItem {
|
||||
item_id: self.new_item_id()?,
|
||||
item: BankItemDetail::Individual(IndividualItemDetail {
|
||||
entity_id: item.id,
|
||||
item: item.item,
|
||||
})
|
||||
}
|
||||
},
|
||||
BankItemEntity::Stacked(items) => {
|
||||
BankItem {
|
||||
item_id: self.new_item_id()?,
|
||||
item: BankItemDetail::Stacked(StackedItemDetail {
|
||||
entity_ids: items.iter().map(|i| i.id).collect(),
|
||||
tool: items.get(0)
|
||||
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||
.item
|
||||
.clone()
|
||||
.as_tool()
|
||||
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
|
||||
let bank_items = join_all(
|
||||
bank.items.into_iter()
|
||||
.map(|item| {
|
||||
let mut citem_state = self.clone();
|
||||
async move {
|
||||
Ok(match item {
|
||||
BankItemEntity::Individual(item) => {
|
||||
BankItem {
|
||||
item_id: citem_state.new_item_id().await?,
|
||||
item: BankItemDetail::Individual(IndividualItemDetail {
|
||||
entity_id: item.id,
|
||||
item: item.item,
|
||||
})
|
||||
}
|
||||
},
|
||||
BankItemEntity::Stacked(items) => {
|
||||
BankItem {
|
||||
item_id: citem_state.new_item_id().await?,
|
||||
item: BankItemDetail::Stacked(StackedItemDetail {
|
||||
entity_ids: items.iter().map(|i| i.id).collect(),
|
||||
tool: items.get(0)
|
||||
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||
.item
|
||||
.clone()
|
||||
.as_tool()
|
||||
.ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
}})
|
||||
.collect::<Vec<_>>())
|
||||
.await
|
||||
.into_iter()
|
||||
.collect::<Result<Vec<_>, ItemStateError>>()?;
|
||||
|
||||
let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?;
|
||||
let bank_state = BankState::new(character.id, BankName("".into()), Bank::new(bank_items), bank_meseta);
|
||||
|
||||
self.character_inventory.insert(character.id, inventory_state);
|
||||
self.character_bank.insert(character.id, bank_state);
|
||||
self.character_inventory
|
||||
.write()
|
||||
.await
|
||||
.insert(character.id, RwLock::new(inventory_state));
|
||||
self.character_bank
|
||||
.write()
|
||||
.await
|
||||
.insert(character.id, RwLock::new(bank_state));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) {
|
||||
pub async fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) {
|
||||
let base_inventory_id = ((area_client.local_client.id() as u32) << 21) | 0x10000;
|
||||
let inventory = self.character_inventory.get_mut(&character.id).unwrap();
|
||||
inventory.initialize_item_ids(base_inventory_id);
|
||||
self.character_inventory
|
||||
.read()
|
||||
.await
|
||||
.get(&character.id)
|
||||
.unwrap()
|
||||
.write()
|
||||
.await
|
||||
.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);
|
||||
if let Some(default_bank ) = default_bank {
|
||||
default_bank.initialize_item_ids(base_bank_id);
|
||||
}
|
||||
self.character_room.insert(character.id, room_id);
|
||||
self.character_floor.insert(character.id, LocalFloor::default());
|
||||
self.room_floor.entry(room_id).or_insert_with(SharedFloor::default);
|
||||
self.character_bank
|
||||
.read()
|
||||
.await
|
||||
.get(&character.id)
|
||||
.unwrap()
|
||||
.write()
|
||||
.await
|
||||
.initialize_item_ids(base_bank_id);
|
||||
self.character_room
|
||||
.write()
|
||||
.await
|
||||
.insert(character.id, room_id);
|
||||
self.character_floor
|
||||
.write()
|
||||
.await
|
||||
.insert(character.id, RwLock::new(LocalFloor::default()));
|
||||
self.room_floor
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id)
|
||||
.or_insert_with(Default::default);
|
||||
}
|
||||
|
||||
pub fn remove_character_from_room(&mut self, character: &CharacterEntity) {
|
||||
self.character_inventory.remove(&character.id);
|
||||
self.character_floor.remove(&character.id);
|
||||
if let Some(room) = self.character_room.remove(&character.id).as_ref() {
|
||||
if self.character_room.iter().any(|(_, r)| r == room) {
|
||||
self.room_floor.remove(room);
|
||||
pub async fn remove_character_from_room(&mut self, character: &CharacterEntity) {
|
||||
self.character_inventory
|
||||
.write()
|
||||
.await
|
||||
.remove(&character.id);
|
||||
self.character_floor
|
||||
.write()
|
||||
.await
|
||||
.remove(&character.id);
|
||||
|
||||
let removed = {
|
||||
self.character_room.write().await.remove(&character.id)
|
||||
};
|
||||
|
||||
if let Some(room) = removed.as_ref() {
|
||||
// TODO: this looks wrong, .all(r != room) maybe?
|
||||
if self.character_room.read().await.iter().any(|(_, r)| r == room) {
|
||||
self.room_floor
|
||||
.write()
|
||||
.await
|
||||
.remove(room);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_floor_item(&self, character_id: &CharacterEntityId, item_id: &ClientItemId) -> Result<(&FloorItem, FloorType), ItemStateError> {
|
||||
let local_floor = self.character_floor.get(character_id).ok_or(ItemStateError::NoCharacter(*character_id))?;
|
||||
let room = self.character_room.get(character_id).ok_or(ItemStateError::NoCharacter(*character_id))?;
|
||||
let shared_floor = self.room_floor.get(room).ok_or(ItemStateError::NoCharacter(*character_id))?;
|
||||
pub async fn get_floor_item(&self, character_id: &CharacterEntityId, item_id: &ClientItemId) -> Result<(FloorItem, FloorType), ItemStateError> {
|
||||
let local_floors = self.character_floor
|
||||
.read()
|
||||
.await;
|
||||
let local_floor = local_floors
|
||||
.get(character_id)
|
||||
.ok_or_else(|| ItemStateError::NoCharacter(*character_id))?
|
||||
.read()
|
||||
.await;
|
||||
let rooms = self.character_room
|
||||
.read()
|
||||
.await;
|
||||
let room = rooms
|
||||
.get(character_id)
|
||||
.ok_or_else(||ItemStateError::NoCharacter(*character_id))?;
|
||||
let shared_floors = self.room_floor
|
||||
.read()
|
||||
.await;
|
||||
let shared_floor = shared_floors
|
||||
.get(room)
|
||||
.ok_or_else(||ItemStateError::NoCharacter(*character_id))?
|
||||
.read()
|
||||
.await;
|
||||
|
||||
local_floor.0
|
||||
.iter()
|
||||
.find(|item| item.item_id == *item_id)
|
||||
.map(|item| (item, FloorType::Local))
|
||||
.map(|item| (item.clone(), FloorType::Local))
|
||||
.or_else(|| {
|
||||
shared_floor.0
|
||||
.iter()
|
||||
.find(|item| item.item_id == *item_id)
|
||||
.map(|item| (item, FloorType::Shared))
|
||||
.map(|item| (item.clone(), FloorType::Shared))
|
||||
})
|
||||
.ok_or(ItemStateError::NoFloorItem(*item_id))
|
||||
.ok_or_else(|| ItemStateError::NoFloorItem(*item_id))
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,7 +378,7 @@ struct ProxiedItemState {
|
||||
character_inventory: HashMap<CharacterEntityId, InventoryState>,
|
||||
character_bank: HashMap<CharacterEntityId, BankState>,
|
||||
|
||||
character_room: HashMap<CharacterEntityId, RoomId>,
|
||||
//character_room: HashMap<CharacterEntityId, RoomId>,
|
||||
character_floor: HashMap<CharacterEntityId, LocalFloor>,
|
||||
room_floor: HashMap<RoomId, SharedFloor>,
|
||||
}
|
||||
@ -325,24 +389,52 @@ pub struct ItemStateProxy<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ItemStateProxy<'a> {
|
||||
pub fn commit(self) {
|
||||
self.item_state.character_inventory.extend(self.proxied_state.character_inventory.clone());
|
||||
self.item_state.character_bank.extend(self.proxied_state.character_bank.clone());
|
||||
self.item_state.character_room.extend(self.proxied_state.character_room.clone());
|
||||
self.item_state.character_floor.extend(self.proxied_state.character_floor.clone());
|
||||
self.item_state.room_floor.extend(self.proxied_state.room_floor);
|
||||
pub async fn commit(self) {
|
||||
async fn copy_back<K, V>(master: &Arc<RwLock<HashMap<K, RwLock<V>>>>,
|
||||
proxy: HashMap<K, V>)
|
||||
where
|
||||
K: Eq + std::hash::Hash,
|
||||
{
|
||||
for (key, value) in proxy {
|
||||
if let Some(element) = master
|
||||
.read()
|
||||
.await
|
||||
.get(&key) {
|
||||
*element
|
||||
.write()
|
||||
.await = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copy_back(&self.item_state.character_inventory, self.proxied_state.character_inventory).await;
|
||||
copy_back(&self.item_state.character_bank, self.proxied_state.character_bank).await;
|
||||
//copy_back(self.item_state.character_room, self.proxied_state.character_room).await;
|
||||
copy_back(&self.item_state.character_floor, self.proxied_state.character_floor).await;
|
||||
copy_back(&self.item_state.room_floor, self.proxied_state.room_floor).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn get_or_clone<K, V>(master: &HashMap<K, V>, proxy: &mut HashMap<K, V>, key: K, err: fn(K) -> ItemStateError) -> Result<V, ItemStateError>
|
||||
async fn get_or_clone<K, V>(master: &Arc<RwLock<HashMap<K, RwLock<V>>>>,
|
||||
proxy: &mut HashMap<K, V>,
|
||||
key: K,
|
||||
err: fn(K) -> ItemStateError) -> Result<V, ItemStateError>
|
||||
where
|
||||
K: Eq + std::hash::Hash + Copy,
|
||||
V: Clone
|
||||
{
|
||||
let existing_element = master.get(&key).ok_or_else(|| err(key))?;
|
||||
let existing_element = master
|
||||
.read()
|
||||
.await
|
||||
.get(&key)
|
||||
.ok_or_else(|| err(key))?
|
||||
.read()
|
||||
.await
|
||||
.clone();
|
||||
Ok(proxy.entry(key)
|
||||
.or_insert_with(|| existing_element.clone()).clone())
|
||||
.or_insert_with(|| existing_element)
|
||||
.clone())
|
||||
|
||||
}
|
||||
|
||||
@ -354,38 +446,44 @@ impl<'a> ItemStateProxy<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inventory(&mut self, character_id: &CharacterEntityId) -> Result<InventoryState, ItemStateError> {
|
||||
get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter)
|
||||
pub async fn inventory(&mut self, character_id: &CharacterEntityId) -> Result<InventoryState, ItemStateError> {
|
||||
get_or_clone(&self.item_state.character_inventory,
|
||||
&mut self.proxied_state.character_inventory,
|
||||
*character_id,
|
||||
ItemStateError::NoCharacter).await
|
||||
}
|
||||
|
||||
pub fn set_inventory(&mut self, inventory: InventoryState) {
|
||||
self.proxied_state.character_inventory.insert(inventory.character_id, inventory);
|
||||
}
|
||||
|
||||
pub fn bank(&mut self, character_id: &CharacterEntityId) -> Result<BankState, ItemStateError> {
|
||||
get_or_clone(&self.item_state.character_bank, &mut self.proxied_state.character_bank, *character_id, ItemStateError::NoCharacter)
|
||||
pub async fn bank(&mut self, character_id: &CharacterEntityId) -> Result<BankState, ItemStateError> {
|
||||
get_or_clone(&self.item_state.character_bank,
|
||||
&mut self.proxied_state.character_bank,
|
||||
*character_id,
|
||||
ItemStateError::NoCharacter).await
|
||||
}
|
||||
|
||||
pub fn set_bank(&mut self, bank: BankState) {
|
||||
self.proxied_state.character_bank.insert(bank.character_id, bank);
|
||||
}
|
||||
|
||||
pub fn floor(&mut self, character_id: &CharacterEntityId) -> Result<FloorState, ItemStateError> {
|
||||
let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, ItemStateError::NoCharacter)?;
|
||||
pub async fn floor(&mut self, character_id: &CharacterEntityId) -> Result<FloorState, ItemStateError> {
|
||||
let room_id = *self.item_state.character_room.read().await.get(character_id).unwrap();
|
||||
Ok(FloorState {
|
||||
character_id: *character_id,
|
||||
local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, ItemStateError::NoCharacter)?,
|
||||
shared: get_or_clone(&self.item_state.room_floor, &mut self.proxied_state.room_floor, room_id, ItemStateError::NoRoom)?,
|
||||
local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, ItemStateError::NoCharacter).await?,
|
||||
shared: get_or_clone(&self.item_state.room_floor, &mut self.proxied_state.room_floor, room_id, ItemStateError::NoRoom).await?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_floor(&mut self, floor: FloorState) {
|
||||
let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, floor.character_id, ItemStateError::NoCharacter).unwrap();
|
||||
pub async fn set_floor(&mut self, floor: FloorState) {
|
||||
let room_id = *self.item_state.character_room.read().await.get(&floor.character_id).unwrap();
|
||||
self.proxied_state.character_floor.insert(floor.character_id, floor.local);
|
||||
self.proxied_state.room_floor.insert(room_id, floor.shared);
|
||||
}
|
||||
|
||||
pub fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> {
|
||||
self.item_state.new_item_id()
|
||||
pub async fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> {
|
||||
self.item_state.new_item_id().await
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ where
|
||||
.act(actions::add_floor_item_to_inventory(character))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -55,7 +55,7 @@ where
|
||||
.act(actions::add_inventory_item_to_shared_floor(character.id, map_area, drop_position))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -79,7 +79,7 @@ where
|
||||
.act(actions::add_inventory_item_to_shared_floor(character.id, map_area, (drop_position.0, 0.0, drop_position.1)))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -104,7 +104,7 @@ where
|
||||
.act(actions::add_meseta_to_shared_floor(character.id, amount, map_area, drop_position))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -126,7 +126,7 @@ where
|
||||
.act(actions::add_meseta_from_bank_to_inventory(character.id, amount))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -148,7 +148,7 @@ where
|
||||
.act(actions::add_meseta_to_bank(character.id, amount))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, ()))
|
||||
}).await
|
||||
}
|
||||
@ -173,7 +173,7 @@ where
|
||||
.act(actions::add_bank_item_to_inventory(character))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -196,7 +196,7 @@ where
|
||||
.act(actions::add_inventory_item_to_bank(character.id))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -217,7 +217,7 @@ where
|
||||
.act(actions::equip_inventory_item(character.id, *item_id, equip_slot))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -238,7 +238,7 @@ where
|
||||
.act(actions::unequip_inventory_item(character.id, *item_id))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -259,7 +259,7 @@ where
|
||||
.act(actions::sort_inventory_items(character.id, item_ids))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -282,7 +282,7 @@ where
|
||||
.act(actions::use_consumed_item(character.clone()))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
*character = new_character;
|
||||
Ok((transaction, ()))
|
||||
}).await
|
||||
@ -306,7 +306,7 @@ where
|
||||
.act(actions::feed_mag_item(character.clone(), *mag_item_id))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, ()))
|
||||
}).await
|
||||
}
|
||||
@ -333,7 +333,7 @@ where
|
||||
.act(actions::add_bought_item_to_inventory(character.id, shop_item, item_id, amount))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -356,7 +356,7 @@ where
|
||||
.act(actions::sell_inventory_item(character.id))
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, result))
|
||||
}).await
|
||||
}
|
||||
@ -424,7 +424,7 @@ where
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, (p1_new_items, p2_new_items)))
|
||||
}).await
|
||||
}
|
||||
@ -446,7 +446,7 @@ where
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, ()))
|
||||
}).await
|
||||
}
|
||||
@ -468,7 +468,7 @@ where
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, floor_item))
|
||||
}).await
|
||||
}
|
||||
@ -494,7 +494,7 @@ where
|
||||
.commit((item_state_proxy, transaction))
|
||||
.await?;
|
||||
|
||||
item_state_proxy.commit();
|
||||
item_state_proxy.commit().await;
|
||||
Ok((transaction, item))
|
||||
}).await
|
||||
}
|
||||
|
@ -4,6 +4,10 @@ use std::time::SystemTime;
|
||||
use thiserror::Error;
|
||||
use crate::common::serverstate::ClientId;
|
||||
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
use futures::{stream, StreamExt};
|
||||
use std::pin::pin;
|
||||
|
||||
pub const MAX_ROOMS: usize = 128;
|
||||
|
||||
pub enum AreaType {
|
||||
@ -87,15 +91,22 @@ pub enum GetLeaderError {
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
||||
#[error("clientlocation")]
|
||||
pub enum ClientLocationError {
|
||||
#[error("create room error {0}")]
|
||||
CreateRoomError(#[from] CreateRoomError),
|
||||
#[error("join room error {0}")]
|
||||
JoinRoomError(#[from] JoinRoomError),
|
||||
#[error("join lobby error {0}")]
|
||||
JoinLobbyError(#[from] JoinLobbyError),
|
||||
#[error("get area error {0}")]
|
||||
GetAreaError(#[from] GetAreaError),
|
||||
#[error("client removal error {0}")]
|
||||
ClientRemovalError(#[from] ClientRemovalError),
|
||||
#[error("get clients error {0}")]
|
||||
GetClientsError(#[from] GetClientsError),
|
||||
#[error("get neighbor error {0}")]
|
||||
GetNeighborError(#[from] GetNeighborError),
|
||||
#[error("get leader error {0}")]
|
||||
GetLeaderError(#[from] GetLeaderError)
|
||||
}
|
||||
|
||||
@ -133,33 +144,48 @@ pub enum RoomLobby {
|
||||
Lobby(LobbyId),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClientLocation {
|
||||
lobbies: [Lobby; 15],
|
||||
rooms: [Option<Room>; MAX_ROOMS],
|
||||
client_location: HashMap<ClientId, RoomLobby>,
|
||||
lobbies: [Arc<RwLock<Lobby>>; 15],
|
||||
rooms: [Option<Arc<RwLock<Room>>>; MAX_ROOMS],
|
||||
client_location: Arc<RwLock<HashMap<ClientId, RoomLobby>>>,
|
||||
}
|
||||
|
||||
impl Default for ClientLocation {
|
||||
fn default() -> ClientLocation {
|
||||
//const RNONE: Option<Arc<RwLock<Room>>> = None;
|
||||
//const LINIT: Arc<RwLock<Lobby>> = Arc::new(RwLock::new(Lobby([None; 12])));
|
||||
ClientLocation {
|
||||
lobbies: [Lobby([None; 12]); 15],
|
||||
rooms: [None; MAX_ROOMS],
|
||||
client_location: HashMap::new(),
|
||||
//lobbies: [LINIT; 15],
|
||||
lobbies: core::array::from_fn(|_| Arc::new(RwLock::new(Lobby([None; 12])))),
|
||||
rooms: core::array::from_fn(|_| None),
|
||||
//rooms: [RNONE; MAX_ROOMS],
|
||||
client_location: Arc::new(RwLock::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ClientLocation {
|
||||
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);
|
||||
pub async fn add_client_to_lobby(&self, id: ClientId, lobby_id: LobbyId) -> Result<(), JoinLobbyError> {
|
||||
{
|
||||
let lobby = self.lobbies
|
||||
.get(lobby_id.0)
|
||||
.ok_or(JoinLobbyError::LobbyDoesNotExist)?
|
||||
.read()
|
||||
.await;
|
||||
if lobby.0.iter().all(|c| c.is_some()) {
|
||||
return Err(JoinLobbyError::LobbyFull);
|
||||
}
|
||||
}
|
||||
self.remove_client_from_area(id);
|
||||
self.remove_client_from_area(id).await;
|
||||
|
||||
let l = self.lobbies.get_mut(lobby.0).ok_or(JoinLobbyError::LobbyDoesNotExist)?;
|
||||
let (index, empty_slot) = l.0.iter_mut()
|
||||
let mut lobby = self.lobbies
|
||||
.get(lobby_id.0)
|
||||
.ok_or(JoinLobbyError::LobbyDoesNotExist)?
|
||||
.write()
|
||||
.await;
|
||||
let (index, empty_slot) = lobby.0.iter_mut()
|
||||
.enumerate()
|
||||
.find(|(_, k)| k.is_none())
|
||||
.ok_or(JoinLobbyError::LobbyFull)?;
|
||||
@ -168,40 +194,45 @@ impl ClientLocation {
|
||||
local_client: LocalClientId(index),
|
||||
time_join: SystemTime::now(),
|
||||
});
|
||||
self.client_location.insert(id, RoomLobby::Lobby(lobby));
|
||||
self.client_location
|
||||
.write()
|
||||
.await
|
||||
.insert(id, RoomLobby::Lobby(lobby_id));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_client_to_next_available_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result<LobbyId, JoinLobbyError> {
|
||||
let l = (0..15)
|
||||
.map(|lobby_index| {
|
||||
pub async fn add_client_to_next_available_lobby(&self, id: ClientId, lobby: LobbyId) -> Result<LobbyId, JoinLobbyError> {
|
||||
pin!(stream::iter(0..15)
|
||||
.filter_map(|lobby_index| async move {
|
||||
let new_lobby = LobbyId((lobby.0 + lobby_index) % 15);
|
||||
(new_lobby, self.add_client_to_lobby(id, new_lobby))
|
||||
})
|
||||
.find(|(_, lobby_option)| {
|
||||
lobby_option.is_ok()
|
||||
})
|
||||
.ok_or(JoinLobbyError::LobbyFull)?;
|
||||
|
||||
Ok(l.0)
|
||||
Some((new_lobby, self.add_client_to_lobby(id, new_lobby).await.ok()?))
|
||||
}))
|
||||
.next()
|
||||
.await
|
||||
.map(|l| l.0)
|
||||
.ok_or(JoinLobbyError::LobbyFull)
|
||||
}
|
||||
|
||||
pub fn create_new_room(&mut self, id: ClientId) -> Result<RoomId, CreateRoomError> {
|
||||
pub async fn create_new_room(&mut self, id: ClientId) -> Result<RoomId, CreateRoomError> {
|
||||
let (index, empty_slot) = self.rooms.iter_mut()
|
||||
.enumerate()
|
||||
.find(|(_, r)| r.is_none())
|
||||
.ok_or(CreateRoomError::NoOpenSlots)?;
|
||||
*empty_slot = Some(Room([None; 4]));
|
||||
self.add_client_to_room(id, RoomId(index)).map_err(|_err| CreateRoomError::JoinError)?;
|
||||
*empty_slot = Some(Arc::new(RwLock::new(Room([None; 4]))));
|
||||
self.add_client_to_room(id, RoomId(index))
|
||||
.await
|
||||
.map_err(|_err| CreateRoomError::JoinError)?;
|
||||
|
||||
Ok(RoomId(index))
|
||||
}
|
||||
|
||||
pub fn add_client_to_room(&mut self, id: ClientId, room: RoomId) -> Result<(), JoinRoomError> {
|
||||
let r = self.rooms.get_mut(room.0)
|
||||
pub async fn add_client_to_room(&mut self, id: ClientId, room: RoomId) -> Result<(), JoinRoomError> {
|
||||
let mut r = self.rooms.get(room.0)
|
||||
.ok_or(JoinRoomError::RoomDoesNotExist)?
|
||||
.as_mut()
|
||||
.ok_or(JoinRoomError::RoomDoesNotExist)?;
|
||||
.as_ref()
|
||||
.ok_or(JoinRoomError::RoomDoesNotExist)?
|
||||
.write()
|
||||
.await;
|
||||
let (index, empty_slot) = r.0.iter_mut()
|
||||
.enumerate()
|
||||
.find(|(_, k)| k.is_none())
|
||||
@ -211,38 +242,51 @@ impl ClientLocation {
|
||||
local_client: LocalClientId(index),
|
||||
time_join: SystemTime::now(),
|
||||
});
|
||||
self.remove_client_from_area(id);
|
||||
self.client_location.insert(id, RoomLobby::Room(room));
|
||||
self.remove_client_from_area(id).await;
|
||||
self.client_location
|
||||
.write()
|
||||
.await
|
||||
.insert(id, RoomLobby::Room(room));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_all_clients_by_client(&self, id: ClientId) -> Result<Vec<AreaClient>, GetNeighborError> {
|
||||
let area = self.client_location.get(&id).ok_or(GetNeighborError::InvalidClient)?;
|
||||
pub async fn get_all_clients_by_client(&self, id: ClientId) -> Result<Vec<AreaClient>, GetNeighborError> {
|
||||
let area = self.client_location
|
||||
.read()
|
||||
.await;
|
||||
let area = area
|
||||
.get(&id)
|
||||
.ok_or(GetNeighborError::InvalidClient)?;
|
||||
match area {
|
||||
RoomLobby::Room(room) => {
|
||||
Ok(self.get_clients_in_room(*room).map_err(|_| GetNeighborError::InvalidArea)?
|
||||
Ok(self.get_clients_in_room(*room).await.map_err(|_| GetNeighborError::InvalidArea)?
|
||||
.into_iter()
|
||||
.collect())
|
||||
},
|
||||
RoomLobby::Lobby(lobby) => {
|
||||
Ok(self.get_clients_in_lobby(*lobby).map_err(|_| GetNeighborError::InvalidArea)?
|
||||
Ok(self.get_clients_in_lobby(*lobby).await.map_err(|_| GetNeighborError::InvalidArea)?
|
||||
.into_iter()
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_client_neighbors(&self, id: ClientId) -> Result<Vec<AreaClient>, GetNeighborError> {
|
||||
let area = self.client_location.get(&id).ok_or(GetNeighborError::InvalidClient)?;
|
||||
pub async fn get_client_neighbors(&self, id: ClientId) -> Result<Vec<AreaClient>, GetNeighborError> {
|
||||
let area = self.client_location
|
||||
.read()
|
||||
.await;
|
||||
let area = area
|
||||
.get(&id)
|
||||
.ok_or(GetNeighborError::InvalidClient)?;
|
||||
match area {
|
||||
RoomLobby::Room(room) => {
|
||||
Ok(self.get_clients_in_room(*room).map_err(|_| GetNeighborError::InvalidArea)?
|
||||
Ok(self.get_clients_in_room(*room).await.map_err(|_| GetNeighborError::InvalidArea)?
|
||||
.into_iter()
|
||||
.filter(|c| c.client != id)
|
||||
.collect())
|
||||
},
|
||||
RoomLobby::Lobby(lobby) => {
|
||||
Ok(self.get_clients_in_lobby(*lobby).map_err(|_| GetNeighborError::InvalidArea)?
|
||||
Ok(self.get_clients_in_lobby(*lobby).await.map_err(|_| GetNeighborError::InvalidArea)?
|
||||
.into_iter()
|
||||
.filter(|c| c.client != id)
|
||||
.collect())
|
||||
@ -250,51 +294,72 @@ impl ClientLocation {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_room_leader(&self, room: RoomId) -> Result<AreaClient, GetLeaderError> {
|
||||
let mut r = self.rooms[room.0]
|
||||
pub async fn get_room_leader(&self, room: RoomId) -> Result<AreaClient, GetLeaderError> {
|
||||
let r = self.rooms[room.0]
|
||||
.as_ref()
|
||||
.ok_or(GetLeaderError::InvalidArea)?
|
||||
.0.iter().flatten()
|
||||
.read()
|
||||
.await;
|
||||
let mut r = r
|
||||
.0
|
||||
.iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
r.sort_by_key(|k| k.time_join);
|
||||
let c = r.get(0).ok_or(GetLeaderError::NoClientInArea)?;
|
||||
let c = r.get(0)
|
||||
.ok_or(GetLeaderError::NoClientInArea)?;
|
||||
Ok(**c)
|
||||
}
|
||||
|
||||
pub fn get_lobby_leader(&self, lobby: LobbyId) -> Result<AreaClient, GetLeaderError> {
|
||||
let mut l = self.lobbies[lobby.0]
|
||||
.0.iter().flatten()
|
||||
pub async fn get_lobby_leader(&self, lobby: LobbyId) -> Result<AreaClient, GetLeaderError> {
|
||||
let l = self.lobbies[lobby.0]
|
||||
.read()
|
||||
.await;
|
||||
let mut l = l
|
||||
.0
|
||||
.iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
l.sort_by_key(|k| k.time_join);
|
||||
let c = l.get(0).ok_or(GetLeaderError::NoClientInArea)?;
|
||||
Ok(**c)
|
||||
}
|
||||
|
||||
pub fn get_area_leader(&self, roomlobby: RoomLobby) -> Result<AreaClient, GetLeaderError> {
|
||||
pub async fn get_area_leader(&self, roomlobby: RoomLobby) -> Result<AreaClient, GetLeaderError> {
|
||||
match roomlobby {
|
||||
RoomLobby::Room(room) => {
|
||||
self.get_room_leader(room)
|
||||
self.get_room_leader(room).await
|
||||
},
|
||||
RoomLobby::Lobby(lobby) => {
|
||||
self.get_lobby_leader(lobby)
|
||||
self.get_lobby_leader(lobby).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_leader_by_client(&self, id: ClientId) -> Result<AreaClient, GetLeaderError> {
|
||||
let area = self.client_location.get(&id).ok_or(GetLeaderError::InvalidClient)?;
|
||||
pub async fn get_leader_by_client(&self, id: ClientId) -> Result<AreaClient, GetLeaderError> {
|
||||
let area = self.client_location
|
||||
.read()
|
||||
.await;
|
||||
let area = area
|
||||
.get(&id)
|
||||
.ok_or(GetLeaderError::InvalidClient)?;
|
||||
match area {
|
||||
RoomLobby::Room(room) => {
|
||||
self.get_room_leader(*room)
|
||||
self.get_room_leader(*room).await
|
||||
},
|
||||
RoomLobby::Lobby(lobby) => {
|
||||
self.get_lobby_leader(*lobby)
|
||||
self.get_lobby_leader(*lobby).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_clients_in_lobby(&self, lobby: LobbyId) -> Result<Vec<AreaClient>, GetClientsError> {
|
||||
Ok(self.lobbies.get(lobby.0).ok_or(GetClientsError::InvalidArea)?.0
|
||||
pub async fn get_clients_in_lobby(&self, lobby: LobbyId) -> Result<Vec<AreaClient>, GetClientsError> {
|
||||
Ok(self.lobbies
|
||||
.get(lobby.0)
|
||||
.ok_or(GetClientsError::InvalidArea)?
|
||||
.read()
|
||||
.await
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|client| {
|
||||
client.map(|c| {
|
||||
@ -303,10 +368,14 @@ impl ClientLocation {
|
||||
}).collect())
|
||||
}
|
||||
|
||||
pub fn get_clients_in_room(&self, room: RoomId) -> Result<Vec<AreaClient>, GetClientsError> {
|
||||
pub async fn get_clients_in_room(&self, room: RoomId) -> Result<Vec<AreaClient>, GetClientsError> {
|
||||
Ok(self.rooms.get(room.0)
|
||||
.ok_or(GetClientsError::InvalidArea)?
|
||||
.ok_or(GetClientsError::InvalidArea)?.0
|
||||
.as_ref()
|
||||
.ok_or(GetClientsError::InvalidArea)?
|
||||
.read()
|
||||
.await
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|client| {
|
||||
client.map(|c| {
|
||||
@ -315,17 +384,26 @@ impl ClientLocation {
|
||||
}).collect())
|
||||
}
|
||||
|
||||
pub fn get_local_client(&self, id: ClientId) -> Result<AreaClient, GetClientsError> {
|
||||
let area = self.client_location.get(&id).ok_or(GetClientsError::InvalidClient)?;
|
||||
pub async fn get_local_client(&self, id: ClientId) -> Result<AreaClient, GetClientsError> {
|
||||
let area = self.client_location
|
||||
.read()
|
||||
.await;
|
||||
let area = area
|
||||
.get(&id)
|
||||
.ok_or(GetClientsError::InvalidClient)?;
|
||||
match area {
|
||||
RoomLobby::Room(room) => {
|
||||
self.get_clients_in_room(*room).map_err(|_| GetClientsError::InvalidArea)?
|
||||
self.get_clients_in_room(*room)
|
||||
.await
|
||||
.map_err(|_| GetClientsError::InvalidArea)?
|
||||
.into_iter()
|
||||
.find(|c| c.client == id)
|
||||
.ok_or(GetClientsError::InvalidClient)
|
||||
},
|
||||
RoomLobby::Lobby(lobby) => {
|
||||
self.get_clients_in_lobby(*lobby).map_err(|_| GetClientsError::InvalidArea)?
|
||||
self.get_clients_in_lobby(*lobby)
|
||||
.await
|
||||
.map_err(|_| GetClientsError::InvalidArea)?
|
||||
.into_iter()
|
||||
.find(|c| c.client == id)
|
||||
.ok_or(GetClientsError::InvalidClient)
|
||||
@ -333,14 +411,17 @@ impl ClientLocation {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_area(&self, id: ClientId) -> Result<RoomLobby, GetAreaError> {
|
||||
self.client_location.get(&id)
|
||||
pub async fn get_area(&self, id: ClientId) -> Result<RoomLobby, GetAreaError> {
|
||||
self.client_location
|
||||
.read()
|
||||
.await
|
||||
.get(&id)
|
||||
.ok_or(GetAreaError::InvalidClient)
|
||||
.map(Clone::clone)
|
||||
}
|
||||
|
||||
pub fn get_room(&self, id: ClientId) -> Result<RoomId, GetAreaError> {
|
||||
if let RoomLobby::Room(room) = self.client_location.get(&id).ok_or(GetAreaError::InvalidClient)? {
|
||||
pub async fn get_room(&self, id: ClientId) -> Result<RoomId, GetAreaError> {
|
||||
if let RoomLobby::Room(room) = self.client_location.read().await.get(&id).ok_or(GetAreaError::InvalidClient)? {
|
||||
Ok(*room)
|
||||
}
|
||||
else {
|
||||
@ -348,8 +429,8 @@ impl ClientLocation {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_lobby(&self, id: ClientId) -> Result<LobbyId, GetAreaError> {
|
||||
if let RoomLobby::Lobby(lobby) = self.client_location.get(&id).ok_or(GetAreaError::InvalidClient)? {
|
||||
pub async fn get_lobby(&self, id: ClientId) -> Result<LobbyId, GetAreaError> {
|
||||
if let RoomLobby::Lobby(lobby) = self.client_location.read().await.get(&id).ok_or(GetAreaError::InvalidClient)? {
|
||||
Ok(*lobby)
|
||||
}
|
||||
else {
|
||||
@ -357,31 +438,41 @@ impl ClientLocation {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_client_from_area(&mut self, id: ClientId) -> Result<(), ClientRemovalError> {
|
||||
let area = self.client_location.get_mut(&id).ok_or(ClientRemovalError::ClientNotInArea)?;
|
||||
let client_list = match area {
|
||||
RoomLobby::Room(room) => {
|
||||
self.rooms[room.0]
|
||||
.as_mut()
|
||||
.map(|r| {
|
||||
r.0.iter_mut()
|
||||
pub async fn remove_client_from_area(&self, id: ClientId) -> Result<(), ClientRemovalError> {
|
||||
fn remove_client<const N: usize>(id: ClientId, client_list : &mut [Option<AreaClient>; N]) {
|
||||
client_list
|
||||
.iter_mut()
|
||||
.filter(|client| {
|
||||
client.map_or(false, |c| {
|
||||
c.client == id
|
||||
})
|
||||
})
|
||||
.for_each(|client| {
|
||||
*client = None
|
||||
});
|
||||
}
|
||||
|
||||
let area = self.client_location
|
||||
.read()
|
||||
.await;
|
||||
let area = area
|
||||
.get(&id)
|
||||
.ok_or(ClientRemovalError::ClientNotInArea)?;
|
||||
match area {
|
||||
RoomLobby::Room(room) => {
|
||||
let r = self.rooms.get(room.0).ok_or(ClientRemovalError::InvalidArea)?;
|
||||
if let Some(r) = r {
|
||||
remove_client(id, &mut r.write().await.0)
|
||||
}
|
||||
else {
|
||||
return Err(ClientRemovalError::InvalidArea)
|
||||
}
|
||||
},
|
||||
RoomLobby::Lobby(lobby) => {
|
||||
Some(self.lobbies[lobby.0].0.iter_mut())
|
||||
remove_client(id, &mut self.lobbies[lobby.0].write().await.0)
|
||||
}
|
||||
};
|
||||
|
||||
client_list
|
||||
.ok_or(ClientRemovalError::InvalidArea)?
|
||||
.filter(|client| {
|
||||
client.map_or(false, |c| {
|
||||
c.client == id
|
||||
})
|
||||
})
|
||||
.for_each(|client| {
|
||||
*client = None
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -397,173 +488,174 @@ impl ClientLocation {
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_add_client_to_lobby() {
|
||||
let mut cl = ClientLocation::default();
|
||||
cl.add_client_to_lobby(ClientId(12), LobbyId(0));
|
||||
cl.add_client_to_lobby(ClientId(13), LobbyId(1));
|
||||
cl.add_client_to_lobby(ClientId(14), LobbyId(0));
|
||||
#[async_std::test]
|
||||
async fn test_add_client_to_lobby() {
|
||||
let cl = ClientLocation::default();
|
||||
cl.add_client_to_lobby(ClientId(12), LobbyId(0)).await.unwrap();
|
||||
cl.add_client_to_lobby(ClientId(13), LobbyId(1)).await.unwrap();
|
||||
cl.add_client_to_lobby(ClientId(14), LobbyId(0)).await.unwrap();
|
||||
|
||||
assert!(cl.get_clients_in_lobby(LobbyId(0)).into_iter().flatten().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
|
||||
assert!(cl.get_clients_in_lobby(LobbyId(0)).await.into_iter().flatten().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
|
||||
(ClientId(12), LocalClientId(0)),
|
||||
(ClientId(14), LocalClientId(1)),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_client_to_full_lobby() {
|
||||
let mut cl = ClientLocation::default();
|
||||
(0..12).for_each(|i| {
|
||||
cl.add_client_to_lobby(ClientId(i), LobbyId(0));
|
||||
});
|
||||
assert!(cl.add_client_to_lobby(ClientId(99), LobbyId(0)) == Err(JoinLobbyError::LobbyFull));
|
||||
#[async_std::test]
|
||||
async fn test_add_client_to_full_lobby() {
|
||||
let cl = ClientLocation::default();
|
||||
for i in 0..12 {
|
||||
cl.add_client_to_lobby(ClientId(i), LobbyId(0)).await.unwrap();
|
||||
}
|
||||
assert!(cl.add_client_to_lobby(ClientId(99), LobbyId(0)).await == Err(JoinLobbyError::LobbyFull));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_client_to_next_available_lobby() {
|
||||
let mut cl = ClientLocation::default();
|
||||
(1..4).for_each(|lobby| {
|
||||
(0..12).for_each(|i| {
|
||||
cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby));
|
||||
});
|
||||
});
|
||||
assert!(cl.add_client_to_next_available_lobby(ClientId(99), LobbyId(1)) == Ok(LobbyId(4)));
|
||||
#[async_std::test]
|
||||
async fn test_add_client_to_next_available_lobby() {
|
||||
let cl = ClientLocation::default();
|
||||
for lobby in 1..4 {
|
||||
for i in 0..12 {
|
||||
cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)).await.unwrap();
|
||||
}
|
||||
}
|
||||
assert!(cl.add_client_to_next_available_lobby(ClientId(99), LobbyId(1)).await == Ok(LobbyId(4)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_to_lobby_when_all_are_full() {
|
||||
let mut cl = ClientLocation::default();
|
||||
(0..15).for_each(|lobby| {
|
||||
(0..12).for_each(|i| {
|
||||
cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby));
|
||||
});
|
||||
});
|
||||
assert!(cl.add_client_to_next_available_lobby(ClientId(99), LobbyId(1)) == Err(JoinLobbyError::LobbyFull));
|
||||
#[async_std::test]
|
||||
async fn test_add_to_lobby_when_all_are_full() {
|
||||
let cl = ClientLocation::default();
|
||||
for lobby in 0..15 {
|
||||
for i in 0..12 {
|
||||
cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)).await.unwrap();
|
||||
}
|
||||
}
|
||||
assert_eq!(cl.add_client_to_next_available_lobby(ClientId(99), LobbyId(1)).await, Err(JoinLobbyError::LobbyFull));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_room() {
|
||||
#[async_std::test]
|
||||
async fn test_new_room() {
|
||||
let mut cl = ClientLocation::default();
|
||||
assert!(cl.create_new_room(ClientId(12)) == Ok(RoomId(0)));
|
||||
assert!(cl.create_new_room(ClientId(12)).await == Ok(RoomId(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_client_to_room() {
|
||||
#[async_std::test]
|
||||
async fn test_add_client_to_room() {
|
||||
let mut cl = ClientLocation::default();
|
||||
let room = cl.create_new_room(ClientId(12)).unwrap();
|
||||
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![
|
||||
let room = cl.create_new_room(ClientId(12)).await.unwrap();
|
||||
assert!(cl.add_client_to_room(ClientId(234), room).await == Ok(()));
|
||||
assert!(cl.get_clients_in_room(room).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
|
||||
(ClientId(12), LocalClientId(0)),
|
||||
(ClientId(234), LocalClientId(1)),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_new_room_slots() {
|
||||
#[async_std::test]
|
||||
async fn test_no_new_room_slots() {
|
||||
let mut cl = ClientLocation::default();
|
||||
for i in 0..128 {
|
||||
cl.create_new_room(ClientId(i));
|
||||
cl.create_new_room(ClientId(i)).await;
|
||||
}
|
||||
assert!(cl.create_new_room(ClientId(234)) == Err(CreateRoomError::NoOpenSlots));
|
||||
assert!(cl.create_new_room(ClientId(234)).await == Err(CreateRoomError::NoOpenSlots));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_joining_full_room() {
|
||||
#[async_std::test]
|
||||
async fn test_joining_full_room() {
|
||||
let mut cl = ClientLocation::default();
|
||||
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(2), room) == Ok(()));
|
||||
assert!(cl.add_client_to_room(ClientId(3), room) == Ok(()));
|
||||
assert!(cl.add_client_to_room(ClientId(234), room) == Err(JoinRoomError::RoomFull));
|
||||
let room = cl.create_new_room(ClientId(0)).await.unwrap();
|
||||
assert!(cl.add_client_to_room(ClientId(1), room).await == Ok(()));
|
||||
assert!(cl.add_client_to_room(ClientId(2), room).await == Ok(()));
|
||||
assert!(cl.add_client_to_room(ClientId(3), room).await == Ok(()));
|
||||
assert!(cl.add_client_to_room(ClientId(234), room).await == Err(JoinRoomError::RoomFull));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adding_client_to_room_removes_from_lobby() {
|
||||
#[async_std::test]
|
||||
async fn test_adding_client_to_room_removes_from_lobby() {
|
||||
let mut cl = ClientLocation::default();
|
||||
cl.add_client_to_lobby(ClientId(93), 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(12), LobbyId(0));
|
||||
cl.add_client_to_lobby(ClientId(93), LobbyId(0)).await;
|
||||
cl.add_client_to_lobby(ClientId(23), LobbyId(0)).await;
|
||||
cl.add_client_to_lobby(ClientId(51), LobbyId(0)).await;
|
||||
cl.add_client_to_lobby(ClientId(12), LobbyId(0)).await;
|
||||
|
||||
let room = cl.create_new_room(ClientId(51)).unwrap();
|
||||
assert!(cl.add_client_to_room(ClientId(93), room) == Ok(()));
|
||||
assert!(cl.get_clients_in_lobby(LobbyId(0)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
|
||||
let room = cl.create_new_room(ClientId(51)).await.unwrap();
|
||||
assert!(cl.add_client_to_room(ClientId(93), room).await == Ok(()));
|
||||
assert!(cl.get_clients_in_lobby(LobbyId(0)).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
|
||||
(ClientId(23), LocalClientId(1)),
|
||||
(ClientId(12), LocalClientId(3)),
|
||||
]);
|
||||
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).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
|
||||
(ClientId(51), LocalClientId(0)),
|
||||
(ClientId(93), LocalClientId(1)),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_getting_neighbors() {
|
||||
let mut cl = ClientLocation::default();
|
||||
cl.add_client_to_lobby(ClientId(93), 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(12), LobbyId(0));
|
||||
#[async_std::test]
|
||||
async fn test_getting_neighbors() {
|
||||
let cl = ClientLocation::default();
|
||||
cl.add_client_to_lobby(ClientId(93), LobbyId(0)).await.unwrap();
|
||||
cl.add_client_to_lobby(ClientId(23), LobbyId(0)).await.unwrap();
|
||||
cl.add_client_to_lobby(ClientId(51), LobbyId(0)).await.unwrap();
|
||||
cl.add_client_to_lobby(ClientId(12), LobbyId(0)).await.unwrap();
|
||||
|
||||
assert!(cl.get_client_neighbors(ClientId(23)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
|
||||
assert!(cl.get_client_neighbors(ClientId(23)).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
|
||||
(ClientId(93), LocalClientId(0)),
|
||||
(ClientId(51), LocalClientId(2)),
|
||||
(ClientId(12), LocalClientId(3)),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_failing_to_join_lobby_does_not_remove_from_current_area() {
|
||||
let mut cl = ClientLocation::default();
|
||||
(0..12).for_each(|i| {
|
||||
cl.add_client_to_lobby(ClientId(i), LobbyId(0));
|
||||
});
|
||||
cl.add_client_to_lobby(ClientId(99), LobbyId(1));
|
||||
cl.add_client_to_lobby(ClientId(99), LobbyId(0));
|
||||
assert!(cl.get_clients_in_lobby(LobbyId(0)).unwrap().len() == 12);
|
||||
assert!(cl.get_clients_in_lobby(LobbyId(1)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
|
||||
(ClientId(99), LocalClientId(0)),
|
||||
]);
|
||||
#[async_std::test]
|
||||
async fn test_failing_to_join_lobby_does_not_remove_from_current_area() {
|
||||
let cl = ClientLocation::default();
|
||||
for i in 0..12 {
|
||||
cl.add_client_to_lobby(ClientId(i), LobbyId(0)).await.unwrap();
|
||||
}
|
||||
assert!(cl.add_client_to_lobby(ClientId(99), LobbyId(1)).await.is_ok());
|
||||
assert!(cl.add_client_to_lobby(ClientId(99), LobbyId(0)).await.is_err());
|
||||
assert_eq!(cl.get_clients_in_lobby(LobbyId(0)).await.unwrap().len(), 12);
|
||||
assert_eq!(
|
||||
cl.get_clients_in_lobby(LobbyId(1)).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>(),
|
||||
vec![(ClientId(99), LocalClientId(0))]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_leader() {
|
||||
let mut cl = ClientLocation::default();
|
||||
cl.add_client_to_lobby(ClientId(93), 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(12), LobbyId(0));
|
||||
#[async_std::test]
|
||||
async fn test_get_leader() {
|
||||
let cl = ClientLocation::default();
|
||||
cl.add_client_to_lobby(ClientId(93), LobbyId(0)).await;
|
||||
cl.add_client_to_lobby(ClientId(23), LobbyId(0)).await;
|
||||
cl.add_client_to_lobby(ClientId(51), LobbyId(0)).await;
|
||||
cl.add_client_to_lobby(ClientId(12), LobbyId(0)).await;
|
||||
|
||||
assert!(cl.get_leader_by_client(ClientId(51)).map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(0))));
|
||||
assert!(cl.get_leader_by_client(ClientId(51)).await.map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(0))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_client_from_room() {
|
||||
#[async_std::test]
|
||||
async fn test_remove_client_from_room() {
|
||||
let mut cl = ClientLocation::default();
|
||||
let room = cl.create_new_room(ClientId(51)).unwrap();
|
||||
cl.add_client_to_room(ClientId(93), room);
|
||||
cl.add_client_to_room(ClientId(23), room);
|
||||
cl.remove_client_from_area(ClientId(51));
|
||||
cl.add_client_to_room(ClientId(12), room);
|
||||
let room = cl.create_new_room(ClientId(51)).await.unwrap();
|
||||
cl.add_client_to_room(ClientId(93), room).await;
|
||||
cl.add_client_to_room(ClientId(23), room).await;
|
||||
cl.remove_client_from_area(ClientId(51)).await;
|
||||
cl.add_client_to_room(ClientId(12), room).await;
|
||||
|
||||
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).await.unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
|
||||
(ClientId(12), LocalClientId(0)),
|
||||
(ClientId(93), LocalClientId(1)),
|
||||
(ClientId(23), LocalClientId(2)),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_leader_changes_on_leader_leaving() {
|
||||
#[async_std::test]
|
||||
async fn test_leader_changes_on_leader_leaving() {
|
||||
let mut cl = ClientLocation::default();
|
||||
let room = cl.create_new_room(ClientId(51)).unwrap();
|
||||
cl.add_client_to_room(ClientId(93), room);
|
||||
cl.add_client_to_room(ClientId(23), room);
|
||||
cl.remove_client_from_area(ClientId(51));
|
||||
cl.add_client_to_room(ClientId(12), room);
|
||||
cl.remove_client_from_area(ClientId(23));
|
||||
cl.add_client_to_room(ClientId(99), room);
|
||||
let room = cl.create_new_room(ClientId(51)).await.unwrap();
|
||||
cl.add_client_to_room(ClientId(93), room).await.unwrap();
|
||||
cl.add_client_to_room(ClientId(23), room).await.unwrap();
|
||||
cl.remove_client_from_area(ClientId(51)).await.unwrap();
|
||||
cl.add_client_to_room(ClientId(12), room).await.unwrap();
|
||||
cl.remove_client_from_area(ClientId(23)).await.unwrap();
|
||||
cl.add_client_to_room(ClientId(99), room).await.unwrap();
|
||||
|
||||
assert!(cl.get_leader_by_client(ClientId(12)).map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(1))));
|
||||
assert!(cl.get_leader_by_client(ClientId(12)).await.map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(1))));
|
||||
}
|
||||
}
|
||||
|
@ -318,8 +318,10 @@ impl fmt::Display for MapArea {
|
||||
pub struct MapAreaLookup(HashMap<u16, MapArea>);
|
||||
|
||||
impl MapAreaLookup {
|
||||
pub fn get_area_map(&self, map_area: u16) -> Result<&MapArea, MapAreaError> {
|
||||
self.0.get(&map_area).ok_or(MapAreaError::UnknownMapArea(map_area))
|
||||
pub fn get_area_map(&self, map_area: u16) -> Result<MapArea, MapAreaError> {
|
||||
self.0.get(&map_area)
|
||||
.copied()
|
||||
.ok_or(MapAreaError::UnknownMapArea(map_area))
|
||||
}
|
||||
|
||||
fn default_ep1_maps() -> MapAreaLookup {
|
||||
|
@ -345,7 +345,6 @@ impl Maps {
|
||||
pub fn roll_monster_appearance(&mut self, rare_monster_table: &RareMonsterAppearTable) {
|
||||
self.enemy_data = self.enemy_data
|
||||
.iter()
|
||||
// .map(|&x| if x.is_some() && x.unwrap().has_rare_appearance() {
|
||||
.map(|&x|
|
||||
if let Some(monster) = x {
|
||||
if monster.has_rare_appearance() {
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub mod ship;
|
||||
pub mod location;
|
||||
pub mod character;
|
||||
pub mod client;
|
||||
pub mod room;
|
||||
pub mod items;
|
||||
pub mod item_stats;
|
||||
|
@ -5,63 +5,84 @@ use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError};
|
||||
use crate::ship::packet::builder::{player_info};
|
||||
use crate::ship::items::state::ItemState;
|
||||
|
||||
use futures::future::join_all;
|
||||
|
||||
pub fn join_lobby(id: ClientId,
|
||||
lobby: LobbyId,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &ItemState)
|
||||
-> Result<JoinLobby, anyhow::Error> {
|
||||
let lobby_clients = client_location.get_clients_in_lobby(lobby).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let playerinfo = lobby_clients.iter()
|
||||
.map(|area_client| {
|
||||
let client = clients.get(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client)).unwrap();
|
||||
player_info(0x100, client, area_client, item_state)
|
||||
});
|
||||
pub async fn join_lobby(id: ClientId,
|
||||
lobby: LobbyId,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &ItemState)
|
||||
-> Result<JoinLobby, ShipError> {
|
||||
let lobby_clients = client_location.get_clients_in_lobby(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let leader = client_location.get_lobby_leader(lobby).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let playerinfo = join_all(
|
||||
lobby_clients.into_iter()
|
||||
.map(|area_client| {
|
||||
let item_state = item_state.clone();
|
||||
async move {
|
||||
clients.with(area_client.client, |client| Box::pin(async move {
|
||||
let inventory = item_state.get_character_inventory(&client.character).await?;
|
||||
Ok(player_info(0x100, client, &area_client, &inventory).await)
|
||||
})).await?
|
||||
}}))
|
||||
.await
|
||||
.into_iter()
|
||||
.collect::<Result<Vec<_>, ShipError>>()?;
|
||||
|
||||
//let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||
let client_block = clients.with(id, |client| Box::pin(async move {
|
||||
client.block as u16
|
||||
})).await?;
|
||||
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let leader = client_location.get_lobby_leader(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(JoinLobby {
|
||||
client: area_client.local_client.id(),
|
||||
leader: leader.local_client.id(),
|
||||
one: 1,
|
||||
lobby: lobby.id(),
|
||||
block: client.block as u16,
|
||||
block: client_block,
|
||||
event: 0,
|
||||
padding: 0,
|
||||
playerinfo: playerinfo.collect(),
|
||||
playerinfo,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_to_lobby(id: ClientId,
|
||||
lobby: LobbyId,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &ItemState)
|
||||
-> Result<AddToLobby, ShipError> {
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let leader = client_location.get_lobby_leader(lobby).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(AddToLobby {
|
||||
flag: 1,
|
||||
client: area_client.local_client.id(),
|
||||
leader: leader.local_client.id(),
|
||||
one: 1,
|
||||
lobby: lobby.id(),
|
||||
block: client.block as u16,
|
||||
event: 0,
|
||||
padding: 0,
|
||||
playerinfo: player_info(0x100, client, &area_client, item_state),
|
||||
})
|
||||
pub async fn add_to_lobby(id: ClientId,
|
||||
lobby: LobbyId,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &ItemState)
|
||||
-> Result<AddToLobby, ShipError> {
|
||||
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let leader = client_location.get_lobby_leader(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
clients.with(id, |client| {
|
||||
let item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
let inventory = item_state.get_character_inventory(&client.character).await?;
|
||||
Ok(AddToLobby {
|
||||
flag: 1,
|
||||
client: area_client.local_client.id(),
|
||||
leader: leader.local_client.id(),
|
||||
one: 1,
|
||||
lobby: lobby.id(),
|
||||
block: client.block as u16,
|
||||
event: 0,
|
||||
padding: 0,
|
||||
playerinfo: player_info(0x100, client, &area_client, &inventory).await,
|
||||
})
|
||||
})}).await?
|
||||
}
|
||||
|
||||
pub fn remove_from_lobby(id: ClientId,
|
||||
client_location: &ClientLocation)
|
||||
-> Result<LeaveLobby, ShipError> {
|
||||
let prev_area_index = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?.local_client.id();
|
||||
let prev_area_leader_index = client_location.get_area_leader(client_location.get_area(id)
|
||||
.map_err(|err| -> ClientLocationError { err.into() })?)
|
||||
pub async fn remove_from_lobby(id: ClientId,
|
||||
client_location: &ClientLocation)
|
||||
-> Result<LeaveLobby, ShipError> {
|
||||
let prev_area_index = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?.local_client.id();
|
||||
let prev_area_leader_index = client_location
|
||||
.get_area_leader(client_location
|
||||
.get_area(id)
|
||||
.await
|
||||
.map_err(|err| -> ClientLocationError { err.into() })?)
|
||||
.await
|
||||
.map_err(|err| -> ClientLocationError { err.into() })?.local_client.id();
|
||||
Ok(LeaveLobby {
|
||||
client: prev_area_index,
|
||||
|
@ -10,7 +10,7 @@ use crate::common::leveltable::LEVEL_TABLE;
|
||||
use crate::ship::character::CharacterBytesBuilder;
|
||||
use crate::ship::ship::ClientState;
|
||||
use crate::ship::location::AreaClient;
|
||||
use crate::ship::items::state::ItemState;
|
||||
use crate::ship::items::inventory::InventoryState;
|
||||
|
||||
pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -> PlayerHeader {
|
||||
PlayerHeader {
|
||||
@ -23,9 +23,8 @@ pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -
|
||||
}
|
||||
}
|
||||
|
||||
pub fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, item_state: &ItemState) -> PlayerInfo {
|
||||
pub async fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, inventory: &InventoryState) -> PlayerInfo {
|
||||
let (level, stats) = LEVEL_TABLE.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||
let inventory = item_state.get_character_inventory(&client.character).unwrap();
|
||||
let character = CharacterBytesBuilder::default()
|
||||
.character(&client.character)
|
||||
.stats(&stats)
|
||||
|
@ -7,26 +7,30 @@ use crate::ship::items::state::ItemState;
|
||||
use crate::ship::packet::builder::{player_header, player_info};
|
||||
use std::convert::TryInto;
|
||||
|
||||
pub fn join_room(id: ClientId,
|
||||
clients: &Clients,
|
||||
client_location: &ClientLocation,
|
||||
room_id: RoomId,
|
||||
room: &RoomState)
|
||||
-> Result<JoinRoom, ShipError> {
|
||||
let all_clients = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let players = all_clients.iter()
|
||||
.enumerate()
|
||||
.fold(Ok([PlayerHeader::default(); 4]), |acc, (i, c)| -> Result<_, ShipError> {
|
||||
let header_client = clients.get(&c.client).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let header_area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
acc.map(|mut a| {
|
||||
a[i] = player_header(0x10000, header_client, &header_area_client);
|
||||
a
|
||||
})
|
||||
})?;
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let leader = client_location.get_room_leader(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
pub async fn join_room(id: ClientId,
|
||||
clients: &Clients,
|
||||
client_location: &ClientLocation,
|
||||
room_id: RoomId,
|
||||
room: &RoomState)
|
||||
-> Result<JoinRoom, ShipError> {
|
||||
let all_clients = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let players = futures::stream::iter(all_clients.iter())
|
||||
.enumerate()
|
||||
.fold::<Result<_, ShipError>, _, _>(Ok([PlayerHeader::default(); 4]), |acc, (i, c)| async move {
|
||||
//let header_client = clients.get(&c.client).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let header_area_client = client_location.get_local_client(id).await.map_err(|err| ShipError::ClientLocationError(err.into()))?;
|
||||
clients.with(c.client, |client| Box::pin(async move {
|
||||
acc.map(|mut a| {
|
||||
a[i] = player_header(0x10000, client, &header_area_client);
|
||||
a
|
||||
})
|
||||
})).await?
|
||||
}).await?;
|
||||
|
||||
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let leader = client_location.get_room_leader(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(JoinRoom {
|
||||
flag: all_clients.len() as u32,
|
||||
maps: room.maps.map_headers(),
|
||||
@ -48,7 +52,7 @@ pub fn join_room(id: ClientId,
|
||||
}
|
||||
|
||||
|
||||
pub fn add_to_room(_id: ClientId,
|
||||
pub async fn add_to_room(_id: ClientId,
|
||||
client: &ClientState,
|
||||
area_client: &AreaClient,
|
||||
leader: &AreaClient,
|
||||
@ -56,7 +60,7 @@ pub fn add_to_room(_id: ClientId,
|
||||
_room_id: RoomId,
|
||||
)
|
||||
-> Result<AddToRoom, ShipError> {
|
||||
|
||||
let inventory = item_state.get_character_inventory(&client.character).await?;
|
||||
Ok(AddToRoom {
|
||||
flag: 1,
|
||||
client: area_client.local_client.id(),
|
||||
@ -66,7 +70,7 @@ pub fn add_to_room(_id: ClientId,
|
||||
block: 0,
|
||||
event: 0,
|
||||
padding: 0,
|
||||
playerinfo: player_info(0x10000, client, area_client, item_state),
|
||||
playerinfo: player_info(0x10000, client, area_client, &inventory).await,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -8,34 +8,38 @@ use crate::ship::items::state::ItemState;
|
||||
use crate::common::interserver::ShipMessage;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn validate_login<EG: EntityGateway>(id: ClientId,
|
||||
pkt: &Login,
|
||||
mut entity_gateway: EG,
|
||||
clients: &mut Clients,
|
||||
item_state: &mut ItemState,
|
||||
shipgate_sender: &Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
|
||||
ship_name: &str,
|
||||
num_blocks: usize)
|
||||
-> Result<Vec<SendShipPacket>, anyhow::Error> {
|
||||
Ok(match get_login_status(&mut entity_gateway, pkt).await {
|
||||
pub async fn validate_login<EG>(id: ClientId,
|
||||
pkt: Login,
|
||||
entity_gateway: &mut EG,
|
||||
clients: &mut Clients,
|
||||
item_state: &mut ItemState,
|
||||
shipgate_sender: &Option<async_std::channel::Sender<ShipMessage>>,
|
||||
ship_name: &str,
|
||||
num_blocks: usize)
|
||||
-> Result<Vec<SendShipPacket>, ShipError>
|
||||
where
|
||||
EG: EntityGateway,
|
||||
{
|
||||
Ok(match get_login_status(entity_gateway, &pkt).await {
|
||||
Ok(user) => {
|
||||
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
|
||||
response.guildcard = user.id.0 as u32;
|
||||
response.team_id = user.team_id.map_or(31, |ti| ti) as u32;
|
||||
response.guildcard = user.id.0;
|
||||
response.team_id = user.team_id.map_or(31, |ti| ti);
|
||||
let characters = entity_gateway.get_characters_by_user(&user).await?;
|
||||
let character = characters
|
||||
.get(pkt.session.character_slot as usize)
|
||||
.ok_or(ShipError::InvalidSlot(id, pkt.session.character_slot as u32))?.as_ref()
|
||||
.ok_or(ShipError::NoCharacterInSlot(id, pkt.session.character_slot as u32))?
|
||||
.ok_or_else(|| ShipError::InvalidSlot(id, pkt.session.character_slot as u32))?
|
||||
.as_ref()
|
||||
.ok_or_else(|| ShipError::NoCharacterInSlot(id, pkt.session.character_slot as u32))?
|
||||
.clone();
|
||||
let settings = entity_gateway.get_user_settings_by_user(&user).await?;
|
||||
|
||||
item_state.load_character(&mut entity_gateway, &character).await?;
|
||||
item_state.load_character(entity_gateway, &character).await?;
|
||||
|
||||
if let Some(shipgate_sender) = shipgate_sender.as_ref() {
|
||||
shipgate_sender(ShipMessage::AddUser(user.id));
|
||||
shipgate_sender.send(ShipMessage::AddUser(user.id)).await?;
|
||||
}
|
||||
clients.insert(id, ClientState::new(user, settings, character, pkt.session));
|
||||
clients.add(id, ClientState::new(user, settings, character, pkt.session)).await;
|
||||
vec![SendShipPacket::LoginResponse(response), SendShipPacket::ShipBlockList(ShipBlockList::new(ship_name, num_blocks))]
|
||||
},
|
||||
Err(err) => {
|
||||
|
@ -4,44 +4,58 @@ use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
||||
use crate::ship::location::{ClientLocation};
|
||||
use crate::entity::gateway::EntityGateway;
|
||||
|
||||
pub fn player_chat(id: ClientId,
|
||||
msg: &PlayerChat,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let cmsg = PlayerChat::new(client.user.id.0, msg.message.clone());
|
||||
use futures::future::join_all;
|
||||
|
||||
Ok(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::PlayerChat(cmsg.clone()))
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn request_infoboard(id: ClientId,
|
||||
pub async fn player_chat(id: ClientId,
|
||||
msg: PlayerChat,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
let area_clients = client_location.get_client_neighbors(id).unwrap();
|
||||
let r = area_clients.iter()
|
||||
.filter_map(|c| {
|
||||
clients.get(&c.client)
|
||||
})
|
||||
.map(|client| {
|
||||
InfoboardResponse {
|
||||
name: libpso::utf8_to_utf16_array!(client.character.name, 16),
|
||||
message: client.character.info_board.as_bytes(),
|
||||
}
|
||||
}).collect();
|
||||
Box::new(vec![(id, SendShipPacket::ViewInfoboardResponse(ViewInfoboardResponse {response: r}))].into_iter())
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let cmsg = clients.with(id, |client| Box::pin(async move {
|
||||
PlayerChat::new(client.user.id.0, msg.message)
|
||||
})).await?;
|
||||
|
||||
Ok(client_location.get_all_clients_by_client(id).await.unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::PlayerChat(cmsg.clone()))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn write_infoboard<EG: EntityGateway>(id: ClientId,
|
||||
new_infoboard: &WriteInfoboard,
|
||||
clients: &mut Clients,
|
||||
mut entity_gateway: EG)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||
client.character.info_board.update_infoboard(new_infoboard);
|
||||
entity_gateway.save_character(&client.character).await.unwrap();
|
||||
Box::new(None.into_iter())
|
||||
pub async fn request_infoboard(id: ClientId,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let area_clients = client_location.get_client_neighbors(id).await.unwrap();
|
||||
let infoboards = join_all(
|
||||
area_clients.iter()
|
||||
.map(|client| async {
|
||||
clients.with(client.client, |client| Box::pin(async move {
|
||||
InfoboardResponse {
|
||||
name: libpso::utf8_to_utf16_array!(client.character.name, 16),
|
||||
message: client.character.info_board.as_bytes(),
|
||||
}
|
||||
})).await
|
||||
}))
|
||||
.await
|
||||
.into_iter()
|
||||
.collect::<Result<Vec<_>, ShipError>>()?;
|
||||
Ok(vec![(id, SendShipPacket::ViewInfoboardResponse(ViewInfoboardResponse {response: infoboards}))])
|
||||
}
|
||||
|
||||
pub async fn write_infoboard<EG>(id: ClientId,
|
||||
new_infoboard: WriteInfoboard,
|
||||
clients: &Clients,
|
||||
entity_gateway: &mut EG)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
Box::pin(async move {
|
||||
client.character.info_board.update_infoboard(&new_infoboard);
|
||||
entity_gateway.save_character(&client.character).await
|
||||
})}).await??;
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
@ -5,9 +5,10 @@ use libpso::packet::ship::*;
|
||||
use libpso::packet::messages::*;
|
||||
use crate::common::leveltable::LEVEL_TABLE;
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops};
|
||||
use crate::ship::location::{ClientLocation, ClientLocationError};
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients, ItemShops};
|
||||
use crate::ship::location::ClientLocation;
|
||||
use crate::ship::drops::ItemDrop;
|
||||
use crate::ship::room::Rooms;
|
||||
use crate::ship::items::ClientItemId;
|
||||
use crate::entity::gateway::EntityGateway;
|
||||
use crate::entity::item;
|
||||
@ -34,71 +35,77 @@ pub enum MessageError {
|
||||
MismatchedTekIds(ClientItemId, ClientItemId),
|
||||
}
|
||||
|
||||
fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(msg.clone()))
|
||||
}))
|
||||
async fn send_to_client(id: ClientId,
|
||||
target: u8,
|
||||
msg: DirectMessage,
|
||||
client_location: &ClientLocation)
|
||||
-> Vec<(ClientId, SendShipPacket)> {
|
||||
client_location.get_all_clients_by_client(id).await.unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(msg.clone()))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn guildcard_send(id: ClientId,
|
||||
guildcard_send: &GuildcardSend,
|
||||
target: u32,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
let client = clients.get(&id).unwrap();
|
||||
let msg = DirectMessage{
|
||||
flag: target,
|
||||
msg: GameMessage::GuildcardRecv(GuildcardRecv {
|
||||
client: guildcard_send.client,
|
||||
target: guildcard_send.target,
|
||||
guildcard: client.user.id.0,
|
||||
name: utf8_to_utf16_array!(client.character.name, 0x18),
|
||||
team: [0; 0x10], // TODO: teams not yet implemented
|
||||
desc: utf8_to_utf16_array!(client.character.guildcard.description, 0x58),
|
||||
one: 1,
|
||||
language: 0, // TODO: add language flag to character
|
||||
section_id: client.character.section_id.into(),
|
||||
class: client.character.char_class.into(),
|
||||
}),
|
||||
};
|
||||
send_to_client(id, target as u8, msg, client_location)
|
||||
pub async fn guildcard_send(id: ClientId,
|
||||
guildcard_send: GuildcardSend,
|
||||
target: u32,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let msg = clients.with(id, |client| Box::pin(async move {
|
||||
DirectMessage{
|
||||
flag: target,
|
||||
msg: GameMessage::GuildcardRecv(GuildcardRecv {
|
||||
client: guildcard_send.client,
|
||||
target: guildcard_send.target,
|
||||
guildcard: client.user.id.0,
|
||||
name: utf8_to_utf16_array!(client.character.name, 0x18),
|
||||
team: [0; 0x10], // TODO: teams not yet implemented
|
||||
desc: utf8_to_utf16_array!(client.character.guildcard.description, 0x58),
|
||||
one: 1,
|
||||
language: 0, // TODO: add language flag to character
|
||||
section_id: client.character.section_id.into(),
|
||||
class: client.character.char_class.into(),
|
||||
}),
|
||||
}
|
||||
})).await?;
|
||||
|
||||
Ok(send_to_client(id, target as u8, msg, client_location).await)
|
||||
}
|
||||
|
||||
pub async fn request_item<EG>(id: ClientId,
|
||||
request_item: &RequestItem,
|
||||
mut entity_gateway: EG,
|
||||
request_item: RequestItem,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
rooms: &mut Rooms,
|
||||
clients: &Clients,
|
||||
rooms: &Rooms,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
{
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get_mut(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.as_mut()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
let room_id = client_location.get_room(id).await?;
|
||||
let monster = rooms.with(room_id, |room| Box::pin(async move {
|
||||
room.maps.enemy_by_id(request_item.enemy_id as usize)
|
||||
})).await??;
|
||||
|
||||
let monster = room.maps.enemy_by_id(request_item.enemy_id as usize)?;
|
||||
if monster.dropped_item {
|
||||
return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id).into())
|
||||
return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id))
|
||||
}
|
||||
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
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| {
|
||||
(area_client, item_drop_type)
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).await?;
|
||||
let client_and_drop = rooms.with_mut(room_id, |room| Box::pin(async move {
|
||||
clients_in_area.into_iter()
|
||||
.filter_map(move |area_client| {
|
||||
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
|
||||
(area_client, item_drop_type)
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
.collect::<Vec<_>>()
|
||||
})).await?;
|
||||
|
||||
let mut item_drop_packets = Vec::new();
|
||||
for (area_client, item_drop) in client_and_drop {
|
||||
let item_drop = ItemDrop {
|
||||
@ -108,100 +115,108 @@ where
|
||||
z: request_item.z,
|
||||
item: item_drop,
|
||||
};
|
||||
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
||||
let floor_item = enemy_drops_item(item_state, &mut entity_gateway, client.character.id, item_drop).await?;
|
||||
let character_id = clients.with(id, |client| Box::pin(async move {
|
||||
client.character.id
|
||||
})).await?;
|
||||
|
||||
let floor_item = enemy_drops_item(item_state, entity_gateway, character_id, item_drop).await?;
|
||||
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
|
||||
|
||||
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))));
|
||||
}
|
||||
|
||||
Ok(Box::new(item_drop_packets.into_iter()))
|
||||
Ok(item_drop_packets)
|
||||
}
|
||||
|
||||
pub async fn pickup_item<EG>(id: ClientId,
|
||||
pickup_item: &PickupItem,
|
||||
mut entity_gateway: EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
pickup_item: PickupItem,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let area_client = client_location.get_local_client(id).await?;
|
||||
let room_id = client_location.get_room(id).await?;
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).await?;
|
||||
|
||||
let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id))?;
|
||||
let remove_item = builder::message::remove_item_from_floor(area_client, item)?;
|
||||
let create_item = match &item.item {
|
||||
FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)?),
|
||||
FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())?),
|
||||
FloorItemDetail::Meseta(_) => None,
|
||||
};
|
||||
|
||||
match pick_up_item(item_state, &mut entity_gateway, &client.character, &ClientItemId(pickup_item.item_id)).await {
|
||||
Ok(trigger_create_item) => {
|
||||
let remove_packets: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = match floor_type {
|
||||
FloorType::Local => {
|
||||
Box::new(vec![(id, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone()))))].into_iter())
|
||||
},
|
||||
FloorType::Shared => {
|
||||
Box::new(clients_in_area.clone().into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone()))))
|
||||
}))
|
||||
},
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id)).await?;
|
||||
let remove_item = builder::message::remove_item_from_floor(area_client, &item)?;
|
||||
let create_item = match &item.item {
|
||||
FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)?),
|
||||
FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())?),
|
||||
FloorItemDetail::Meseta(_) => None,
|
||||
};
|
||||
|
||||
Ok(Box::new(remove_packets
|
||||
.chain(clients_in_area.into_iter().
|
||||
filter_map(move |c| {
|
||||
match trigger_create_item {
|
||||
TriggerCreateItem::Yes => create_item.clone().map(|ci| (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(ci))))),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
))))
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("character {:?} could not pick up item: {:?}", client.character.id, err);
|
||||
Ok(Box::new(None.into_iter()))
|
||||
},
|
||||
}
|
||||
match pick_up_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(pickup_item.item_id)).await {
|
||||
Ok(trigger_create_item) => {
|
||||
let remove_packets: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = match floor_type {
|
||||
FloorType::Local => {
|
||||
Box::new(vec![(id, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone()))))].into_iter())
|
||||
},
|
||||
FloorType::Shared => {
|
||||
Box::new(clients_in_area.clone().into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone()))))
|
||||
}))
|
||||
},
|
||||
};
|
||||
|
||||
Ok(remove_packets
|
||||
.chain(clients_in_area.into_iter()
|
||||
.filter_map(move |c| {
|
||||
match trigger_create_item {
|
||||
TriggerCreateItem::Yes => create_item.clone().map(|ci| (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(ci))))),
|
||||
_ => None
|
||||
}
|
||||
}))
|
||||
.collect())
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("character {:?} could not pick up item: {:?}", client.character.id, err);
|
||||
Ok(Vec::new())
|
||||
},
|
||||
}
|
||||
})}).await?
|
||||
}
|
||||
|
||||
pub async fn request_box_item<EG>(id: ClientId,
|
||||
box_drop_request: &BoxDropRequest,
|
||||
mut entity_gateway: EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
rooms: &mut Rooms,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
box_drop_request: BoxDropRequest,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
rooms: &Rooms,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static
|
||||
{
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get_mut(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.as_mut()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
let room_id = client_location.get_room(id).await?;
|
||||
let box_object = rooms.with(room_id, |room| Box::pin(async move {
|
||||
room.maps.object_by_id(box_drop_request.object_id as usize)
|
||||
})).await??;
|
||||
|
||||
let box_object = room.maps.object_by_id(box_drop_request.object_id as usize)?;
|
||||
if box_object.dropped_item {
|
||||
return Err(ShipError::BoxAlreadyDroppedItem(id, box_drop_request.object_id).into())
|
||||
return Err(ShipError::BoxAlreadyDroppedItem(id, box_drop_request.object_id))
|
||||
}
|
||||
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
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| {
|
||||
(area_client, item_drop_type)
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).await?;
|
||||
|
||||
let client_and_drop = rooms.with_mut(room_id, |room| Box::pin(async move {
|
||||
clients_in_area.into_iter()
|
||||
.filter_map(move |area_client| {
|
||||
room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
|
||||
(area_client, item_drop_type)
|
||||
})
|
||||
})
|
||||
});
|
||||
.collect::<Vec<_>>()
|
||||
})).await?;
|
||||
|
||||
let mut item_drop_packets = Vec::new();
|
||||
for (area_client, item_drop) in client_and_drop {
|
||||
@ -212,94 +227,145 @@ EG: EntityGateway
|
||||
z: box_drop_request.z,
|
||||
item: item_drop,
|
||||
};
|
||||
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
||||
let floor_item = enemy_drops_item(item_state, &mut entity_gateway, client.character.id, item_drop).await?;
|
||||
//let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
||||
let character_id = clients.with(area_client.client, |client| Box::pin(async move {
|
||||
client.character.id
|
||||
})).await?;
|
||||
let floor_item = enemy_drops_item(item_state, entity_gateway, character_id, item_drop).await?;
|
||||
//let floor_item = enemy_drops_item(item_state, &mut entity_gateway, client.character.id, item_drop).await?;
|
||||
let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?;
|
||||
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))))
|
||||
}
|
||||
|
||||
Ok(Box::new(item_drop_packets.into_iter()))
|
||||
Ok(item_drop_packets)
|
||||
}
|
||||
|
||||
|
||||
// item_manager is not mutable in this, but for reasons I don't quite understand it requires the unique access of it to compile here
|
||||
pub async fn send_bank_list(id: ClientId,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
{
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let bank = item_state.get_character_bank(&client.character)?;
|
||||
let bank_items_pkt = builder::message::bank_item_list(bank);
|
||||
Ok(Box::new(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))].into_iter()))
|
||||
let bank = clients.with(id, |client| {
|
||||
let item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
item_state.get_character_bank(&client.character).await
|
||||
})
|
||||
}).await??;
|
||||
let bank_items_pkt = builder::message::bank_item_list(&bank);
|
||||
Ok(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))])
|
||||
}
|
||||
|
||||
pub async fn bank_interaction<EG>(id: ClientId,
|
||||
bank_interaction: &BankInteraction,
|
||||
mut entity_gateway: EG,
|
||||
bank_interaction: BankInteraction,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let other_clients_in_area = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let bank_action_pkts = match bank_interaction.action {
|
||||
BANK_ACTION_DEPOSIT => {
|
||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||
deposit_meseta(item_state, &mut entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
|
||||
Vec::new()
|
||||
}
|
||||
else {
|
||||
deposit_item(item_state, &mut entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
|
||||
let player_no_longer_has_item = builder::message::player_no_longer_has_item(area_client, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32);
|
||||
vec![SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)))]
|
||||
}
|
||||
},
|
||||
BANK_ACTION_WITHDRAW => {
|
||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||
withdraw_meseta(item_state, &mut entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
|
||||
Vec::new()
|
||||
}
|
||||
else {
|
||||
let item_added_to_inventory = withdraw_item(item_state, &mut entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
|
||||
let item_created = builder::message::create_withdrawn_inventory_item2(area_client, &item_added_to_inventory)?;
|
||||
vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))]
|
||||
}
|
||||
},
|
||||
_ => { // TODO: error?
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Box::new(other_clients_in_area.into_iter()
|
||||
.flat_map(move |c| {
|
||||
bank_action_pkts.clone().into_iter()
|
||||
.map(move |pkt| {
|
||||
(c.client, pkt)
|
||||
})
|
||||
})
|
||||
))
|
||||
let area_client = client_location.get_local_client(id).await?;
|
||||
let other_clients_in_area = client_location.get_all_clients_by_client(id).await?;
|
||||
|
||||
let bank_action_pkts = clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
Ok::<_, ShipError>(match bank_interaction.action {
|
||||
BANK_ACTION_DEPOSIT => {
|
||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||
deposit_meseta(&mut item_state, &mut entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
|
||||
Vec::new()
|
||||
}
|
||||
else {
|
||||
deposit_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
|
||||
let player_no_longer_has_item = builder::message::player_no_longer_has_item(area_client, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32);
|
||||
vec![SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)))]
|
||||
}
|
||||
},
|
||||
BANK_ACTION_WITHDRAW => {
|
||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||
withdraw_meseta(&mut item_state, &mut entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
|
||||
Vec::new()
|
||||
}
|
||||
else {
|
||||
let item_added_to_inventory = withdraw_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?;
|
||||
let item_created = builder::message::create_withdrawn_inventory_item2(area_client, &item_added_to_inventory)?;
|
||||
vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))]
|
||||
}
|
||||
},
|
||||
_ => { // TODO: error?
|
||||
Vec::new()
|
||||
}
|
||||
})
|
||||
})
|
||||
}).await??;
|
||||
|
||||
Ok(other_clients_in_area.into_iter()
|
||||
.flat_map(move |c| {
|
||||
bank_action_pkts.clone().into_iter()
|
||||
.map(move |pkt| {
|
||||
(c.client, pkt)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn shop_request(id: ClientId,
|
||||
shop_request: &ShopRequest,
|
||||
shop_request: ShopRequest,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
clients: &Clients,
|
||||
rooms: &Rooms,
|
||||
shops: &mut ItemShops)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
shops: &ItemShops)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
//let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let room_id = client_location.get_room(id).await?;
|
||||
/*
|
||||
let room = rooms.get(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.as_ref()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp) as usize;
|
||||
*/
|
||||
let difficulty = rooms.with(room_id, |room| Box::pin(async move {
|
||||
room.mode.difficulty()
|
||||
})).await?;
|
||||
let shop_list = clients.with_mut(id, |client| {
|
||||
let mut shops = shops.clone();
|
||||
Box::pin(async move {
|
||||
let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp) as usize;
|
||||
match shop_request.shop_type {
|
||||
SHOP_OPTION_WEAPON => {
|
||||
client.weapon_shop = shops.weapon_shop.get_mut(&(difficulty, client.character.section_id))
|
||||
.ok_or(ShipError::ShopError)?
|
||||
.lock()
|
||||
.await
|
||||
.generate_weapon_list(level);
|
||||
Ok(builder::message::shop_list(shop_request.shop_type, &client.weapon_shop))
|
||||
},
|
||||
SHOP_OPTION_TOOL => {
|
||||
client.tool_shop = shops.tool_shop
|
||||
.lock()
|
||||
.await
|
||||
.generate_tool_list(level);
|
||||
Ok(builder::message::shop_list(shop_request.shop_type, &client.tool_shop))
|
||||
},
|
||||
SHOP_OPTION_ARMOR => {
|
||||
client.armor_shop = shops.armor_shop
|
||||
.lock()
|
||||
.await
|
||||
.generate_armor_list(level);
|
||||
Ok(builder::message::shop_list(shop_request.shop_type, &client.armor_shop))
|
||||
},
|
||||
_ => {
|
||||
Err(ShipError::ShopError)
|
||||
}
|
||||
}
|
||||
})}).await??;
|
||||
/*
|
||||
let shop_list = match shop_request.shop_type {
|
||||
SHOP_OPTION_WEAPON => {
|
||||
client.weapon_shop = shops.weapon_shop.get_mut(&(room.mode.difficulty(), client.character.section_id))
|
||||
@ -319,64 +385,69 @@ pub async fn shop_request(id: ClientId,
|
||||
return Err(ShipError::ShopError.into())
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
Ok(Box::new(vec![(id, SendShipPacket::Message(Message::new(GameMessage::ShopList(shop_list))))].into_iter()))
|
||||
Ok(vec![(id, SendShipPacket::Message(Message::new(GameMessage::ShopList(shop_list))))])
|
||||
}
|
||||
|
||||
|
||||
pub async fn buy_item<EG>(id: ClientId,
|
||||
buy_item: &BuyItem,
|
||||
mut entity_gateway: EG,
|
||||
buy_item: BuyItem,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let area_client = client_location.get_local_client(id).await?;
|
||||
|
||||
let (item, remove): (&(dyn ShopItem + Send + Sync), bool) = match buy_item.shop_type {
|
||||
SHOP_OPTION_WEAPON => {
|
||||
(client.weapon_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?, false)
|
||||
},
|
||||
SHOP_OPTION_TOOL => {
|
||||
let item = client.tool_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?;
|
||||
let remove = matches!(item, ToolShopItem::Tech(_));
|
||||
(item, remove)
|
||||
},
|
||||
SHOP_OPTION_ARMOR => {
|
||||
let item = client.armor_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?;
|
||||
let remove = matches!(item, ArmorShopItem::Unit(_));
|
||||
(item, remove)
|
||||
},
|
||||
_ => {
|
||||
return Err(ShipError::ShopError.into())
|
||||
}
|
||||
};
|
||||
let create = clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
let (item, remove): (&(dyn ShopItem + Send + Sync), bool) = match buy_item.shop_type {
|
||||
SHOP_OPTION_WEAPON => {
|
||||
(client.weapon_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?, false)
|
||||
},
|
||||
SHOP_OPTION_TOOL => {
|
||||
let item = client.tool_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?;
|
||||
let remove = matches!(item, ToolShopItem::Tech(_));
|
||||
(item, remove)
|
||||
},
|
||||
SHOP_OPTION_ARMOR => {
|
||||
let item = client.armor_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?;
|
||||
let remove = matches!(item, ArmorShopItem::Unit(_));
|
||||
(item, remove)
|
||||
},
|
||||
_ => {
|
||||
return Err(ShipError::ShopError)
|
||||
}
|
||||
};
|
||||
|
||||
let inventory_item = buy_shop_item(item_state, &mut entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as u32).await?;
|
||||
let create = builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)?;
|
||||
let inventory_item = buy_shop_item(&mut item_state, &mut entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as u32).await?;
|
||||
|
||||
if remove {
|
||||
match buy_item.shop_type {
|
||||
SHOP_OPTION_TOOL => {
|
||||
client.tool_shop.remove(buy_item.shop_index as usize);
|
||||
},
|
||||
SHOP_OPTION_ARMOR => {
|
||||
client.armor_shop.remove(buy_item.shop_index as usize);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let other_clients_in_area = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(Box::new(other_clients_in_area.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create.clone()))))
|
||||
})))
|
||||
if remove {
|
||||
match buy_item.shop_type {
|
||||
SHOP_OPTION_TOOL => {
|
||||
client.tool_shop.remove(buy_item.shop_index as usize);
|
||||
},
|
||||
SHOP_OPTION_ARMOR => {
|
||||
client.armor_shop.remove(buy_item.shop_index as usize);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)
|
||||
})}).await??;
|
||||
|
||||
let other_clients_in_area = client_location.get_client_neighbors(id).await?;
|
||||
Ok(other_clients_in_area.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create.clone()))))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
|
||||
@ -390,16 +461,17 @@ const TEK_PERCENT_MODIFIER: [item::weapon::TekPercentModifier; 5] = [item::weapo
|
||||
item::weapon::TekPercentModifier::MinusMinus];
|
||||
|
||||
pub async fn request_tek_item<EG>(id: ClientId,
|
||||
tek_request: &TekRequest,
|
||||
mut entity_gateway: EG,
|
||||
clients: &mut Clients,
|
||||
tek_request: TekRequest,
|
||||
entity_gateway: &mut EG,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
//let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
|
||||
// TODO: secids have different mod rates
|
||||
let (grind_mod, special_mod, percent_mod) = {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
@ -409,63 +481,73 @@ where
|
||||
(grind_mod, special_mod, percent_mod)
|
||||
};
|
||||
|
||||
client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod));
|
||||
let preview_pkt = clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod));
|
||||
|
||||
let inventory = item_state.get_character_inventory(&client.character)?;
|
||||
let item = inventory.get_by_client_id(&ClientItemId(tek_request.item_id))
|
||||
.ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?;
|
||||
let mut weapon = *item.item.as_individual()
|
||||
.ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?
|
||||
.as_weapon()
|
||||
.ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?;
|
||||
let inventory = item_state.get_character_inventory(&client.character).await?;
|
||||
let item = inventory.get_by_client_id(&ClientItemId(tek_request.item_id))
|
||||
.ok_or_else(|| ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?;
|
||||
let mut weapon = *item.item.as_individual()
|
||||
.ok_or_else(|| ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?
|
||||
.as_weapon()
|
||||
.ok_or_else(|| ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?;
|
||||
|
||||
weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked {
|
||||
special: special_mod,
|
||||
percent: percent_mod,
|
||||
grind: grind_mod,
|
||||
});
|
||||
weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked {
|
||||
special: special_mod,
|
||||
percent: percent_mod,
|
||||
grind: grind_mod,
|
||||
});
|
||||
|
||||
take_meseta(item_state, &mut entity_gateway, &client.character.id, item::Meseta(100)).await?;
|
||||
take_meseta(&mut item_state, &mut entity_gateway, &client.character.id, item::Meseta(100)).await?;
|
||||
builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)
|
||||
})}).await??;
|
||||
|
||||
let preview_pkt = builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?;
|
||||
|
||||
Ok(Box::new(vec![(id, SendShipPacket::Message(Message::new(GameMessage::TekPreview(preview_pkt))))].into_iter()))
|
||||
Ok(vec![(id, SendShipPacket::Message(Message::new(GameMessage::TekPreview(preview_pkt))))])
|
||||
}
|
||||
|
||||
pub async fn accept_tek_item<EG>(id: ClientId,
|
||||
tek_accept: &TekAccept,
|
||||
mut entity_gateway: EG,
|
||||
tek_accept: TekAccept,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let area_client = client_location.get_local_client(id).await?;
|
||||
let neighbors = client_location.get_client_neighbors(id).await?;
|
||||
|
||||
if let Some((item_id, special_mod, percent_mod, grind_mod)) = client.tek {
|
||||
if item_id.0 != tek_accept.item_id {
|
||||
return Err(MessageError::MismatchedTekIds(item_id, ClientItemId(tek_accept.item_id)).into());
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
if let Some((item_id, special_mod, percent_mod, grind_mod)) = client.tek {
|
||||
if item_id.0 != tek_accept.item_id {
|
||||
return Err(MessageError::MismatchedTekIds(item_id, ClientItemId(tek_accept.item_id)).into());
|
||||
}
|
||||
|
||||
let modifier = item::weapon::WeaponModifier::Tekked {
|
||||
special: special_mod,
|
||||
percent: percent_mod,
|
||||
grind: grind_mod,
|
||||
};
|
||||
let weapon = apply_modifier(&mut item_state, &mut entity_gateway, &client.character, item_id, item::ItemModifier::WeaponModifier(modifier)).await?;
|
||||
|
||||
let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon)?;
|
||||
|
||||
Ok(neighbors.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item_pkt.clone()))))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
let modifier = item::weapon::WeaponModifier::Tekked {
|
||||
special: special_mod,
|
||||
percent: percent_mod,
|
||||
grind: grind_mod,
|
||||
};
|
||||
let weapon = apply_modifier(item_state, &mut entity_gateway, &client.character, item_id, item::ItemModifier::WeaponModifier(modifier)).await?;
|
||||
|
||||
let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon)?;
|
||||
|
||||
let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(Box::new(neighbors.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item_pkt.clone()))))
|
||||
})))
|
||||
}
|
||||
else {
|
||||
Err(MessageError::InvalidTek(ClientItemId(tek_accept.item_id)).into())
|
||||
}
|
||||
else {
|
||||
Err(MessageError::InvalidTek(ClientItemId(tek_accept.item_id)).into())
|
||||
}
|
||||
})}).await?
|
||||
}
|
||||
|
@ -1,61 +1,66 @@
|
||||
use libpso::packet::ship::*;
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::common::leveltable::LEVEL_TABLE;
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms};
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
||||
use crate::ship::room::Rooms;
|
||||
use crate::ship::character::{FullCharacterBytesBuilder};
|
||||
use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId};
|
||||
use crate::ship::packet;
|
||||
use crate::ship::items::state::ItemState;
|
||||
use crate::entity::gateway::EntityGateway;
|
||||
use crate::ship::map::MapArea;
|
||||
use futures::future::join_all;
|
||||
|
||||
// this function needs a better home
|
||||
pub fn block_selected(id: ClientId,
|
||||
pkt: &MenuSelect,
|
||||
clients: &mut Clients,
|
||||
item_state: &ItemState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
client.block = pkt.item as usize - 1;
|
||||
|
||||
let (level, stats) = LEVEL_TABLE.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||
|
||||
let inventory = item_state.get_character_inventory(&client.character).unwrap();
|
||||
let bank = item_state.get_character_bank(&client.character).unwrap();
|
||||
|
||||
let fc = FullCharacterBytesBuilder::default()
|
||||
.character(&client.character)
|
||||
.stats(&stats)
|
||||
.level(level)
|
||||
.meseta(inventory.meseta)
|
||||
.inventory(inventory)
|
||||
.bank(bank)
|
||||
.keyboard_config(&client.character.keyboard_config.as_bytes())
|
||||
.gamepad_config(&client.character.gamepad_config.as_bytes())
|
||||
.symbol_chat(&client.settings.settings.symbol_chats)
|
||||
.tech_menu(&client.character.tech_menu.as_bytes())
|
||||
.option_flags(client.character.option_flags)
|
||||
.build();
|
||||
|
||||
Ok(vec![
|
||||
(id, SendShipPacket::FullCharacter(Box::new(FullCharacter {
|
||||
character: fc,
|
||||
}))),
|
||||
(id, SendShipPacket::CharDataRequest(CharDataRequest {})),
|
||||
(id, SendShipPacket::LobbyList(LobbyList::new())),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn send_player_to_lobby(id: ClientId,
|
||||
_pkt: &CharData,
|
||||
client_location: &mut ClientLocation,
|
||||
pub async fn block_selected(id: ClientId,
|
||||
pkt: MenuSelect,
|
||||
clients: &Clients,
|
||||
item_state: &ItemState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).map_err(|_| ShipError::TooManyClients)?;
|
||||
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state)?;
|
||||
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state)?;
|
||||
let neighbors = client_location.get_client_neighbors(id).unwrap();
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
clients.with_mut(id, |client| {
|
||||
let item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
client.block = pkt.item as usize - 1;
|
||||
|
||||
let (level, stats) = LEVEL_TABLE.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||
|
||||
let inventory = item_state.get_character_inventory(&client.character).await?;
|
||||
let bank = item_state.get_character_bank(&client.character).await?;
|
||||
|
||||
let fc = FullCharacterBytesBuilder::default()
|
||||
.character(&client.character)
|
||||
.stats(&stats)
|
||||
.level(level)
|
||||
.meseta(inventory.meseta)
|
||||
.inventory(&inventory)
|
||||
.bank(&bank)
|
||||
.keyboard_config(&client.character.keyboard_config.as_bytes())
|
||||
.gamepad_config(&client.character.gamepad_config.as_bytes())
|
||||
.symbol_chat(&client.settings.settings.symbol_chats)
|
||||
.tech_menu(&client.character.tech_menu.as_bytes())
|
||||
.option_flags(client.character.option_flags)
|
||||
.build();
|
||||
|
||||
Ok(vec![
|
||||
(id, SendShipPacket::FullCharacter(Box::new(FullCharacter {
|
||||
character: fc,
|
||||
}))),
|
||||
(id, SendShipPacket::CharDataRequest(CharDataRequest {})),
|
||||
(id, SendShipPacket::LobbyList(LobbyList::new())),
|
||||
])
|
||||
})}).await?
|
||||
}
|
||||
|
||||
pub async fn send_player_to_lobby(id: ClientId,
|
||||
_pkt: CharData,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &ItemState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).await.map_err(|_| ShipError::TooManyClients)?;
|
||||
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state).await?;
|
||||
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state).await?;
|
||||
let neighbors = client_location.get_client_neighbors(id).await.unwrap();
|
||||
Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))]
|
||||
.into_iter()
|
||||
.chain(neighbors.into_iter()
|
||||
@ -63,16 +68,18 @@ pub fn send_player_to_lobby(id: ClientId,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn change_lobby<EG: EntityGateway>(id: ClientId,
|
||||
requested_lobby: u32,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
ship_rooms: &mut Rooms,
|
||||
mut entity_gateway: EG)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let prev_area = client_location.get_area(id).map_err(|err| -> ClientLocationError {err.into()})?;
|
||||
pub async fn change_lobby<EG>(id: ClientId,
|
||||
requested_lobby: u32,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
rooms: &Rooms,
|
||||
entity_gateway: &mut EG)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let prev_area = client_location.get_area(id).await.map_err(|err| -> ClientLocationError {err.into()})?;
|
||||
match prev_area {
|
||||
RoomLobby::Lobby(old_lobby) => {
|
||||
if old_lobby.0 == requested_lobby as usize {
|
||||
@ -80,30 +87,39 @@ pub async fn change_lobby<EG: EntityGateway>(id: ClientId,
|
||||
}
|
||||
},
|
||||
RoomLobby::Room(old_room) => {
|
||||
if client_location.get_client_neighbors(id)?.is_empty() {
|
||||
ship_rooms[old_room.0] = None;
|
||||
if client_location.get_client_neighbors(id).await?.is_empty() {
|
||||
rooms.remove(old_room).await;
|
||||
}
|
||||
item_state.remove_character_from_room(&client.character);
|
||||
clients.with(id, |client| {
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
item_state.remove_character_from_room(&client.character).await;
|
||||
})}).await?;
|
||||
},
|
||||
}
|
||||
let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location)?;
|
||||
let old_neighbors = client_location.get_client_neighbors(id).unwrap();
|
||||
let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location).await?;
|
||||
let old_neighbors = client_location.get_client_neighbors(id).await.unwrap();
|
||||
let mut lobby = LobbyId(requested_lobby as usize);
|
||||
if client_location.add_client_to_lobby(id, lobby).is_err() {
|
||||
if client_location.add_client_to_lobby(id, lobby).await.is_err() {
|
||||
match prev_area {
|
||||
RoomLobby::Lobby(_lobby) => {
|
||||
let dialog = SmallDialog::new(String::from("Lobby is full."));
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(dialog))])
|
||||
}
|
||||
RoomLobby::Room(_room) => {
|
||||
lobby = client_location.add_client_to_next_available_lobby(id, lobby).map_err(|_| ShipError::TooManyClients)?;
|
||||
lobby = client_location.add_client_to_next_available_lobby(id, lobby).await.map_err(|_| ShipError::TooManyClients)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
item_state.load_character(&mut entity_gateway, &client.character).await?;
|
||||
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state)?;
|
||||
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state)?;
|
||||
let neighbors = client_location.get_client_neighbors(id).unwrap();
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
item_state.load_character(&mut entity_gateway, &client.character).await
|
||||
})}).await??;
|
||||
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state).await?;
|
||||
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state).await?;
|
||||
let neighbors = client_location.get_client_neighbors(id).await?;
|
||||
Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))]
|
||||
.into_iter()
|
||||
.chain(neighbors.into_iter()
|
||||
@ -113,42 +129,45 @@ pub async fn change_lobby<EG: EntityGateway>(id: ClientId,
|
||||
.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)?;
|
||||
pub async fn remove_from_lobby(id: ClientId,
|
||||
client_location: &mut ClientLocation)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let area_client = client_location.get_local_client(id).await?;
|
||||
let neighbors = client_location.get_client_neighbors(id).await?;
|
||||
let leader = client_location.get_leader_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let leave_lobby_pkt = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()));
|
||||
|
||||
client_location.remove_client_from_area(id)?;
|
||||
client_location.remove_client_from_area(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(neighbors.into_iter().map(|n| {
|
||||
(n.client, leave_lobby_pkt.clone())
|
||||
}).collect())
|
||||
}
|
||||
|
||||
pub fn get_room_tab_info(id: ClientId,
|
||||
pkt: &MenuDetail,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients,
|
||||
rooms: &mut Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
pub async fn get_room_tab_info(id: ClientId,
|
||||
pkt: MenuDetail,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let room_id = RoomId(pkt.item as usize);
|
||||
if let Some(_room) = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))? {
|
||||
let mut room_info = String::new();
|
||||
let clients_in_room = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
for client in clients_in_room {
|
||||
let cs = clients.get(&client.client).ok_or(ShipError::ClientNotFound(client.client))?;
|
||||
let gc = cs.user.guildcard;
|
||||
let name = &cs.character.name;
|
||||
let cc = cs.character.char_class;
|
||||
let lv = LEVEL_TABLE.get_level_from_exp(cc, cs.character.exp);
|
||||
let floor = cs.area.unwrap_or(MapArea::Pioneer2Ep1);
|
||||
|
||||
room_info += format!("{} Lv{} {}\n{} {}\n", gc,lv,name,cc,floor).as_str();
|
||||
}
|
||||
Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new(room_info)))])
|
||||
} else {
|
||||
Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new("Game is no longer active".into())))])
|
||||
let clients_in_room = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room_info = if clients_in_room.is_empty() {
|
||||
String::from("Game is no longer active")
|
||||
}
|
||||
else {
|
||||
join_all(clients_in_room.iter()
|
||||
.map(|clientl| async move {
|
||||
clients.with(clientl.client, |client| Box::pin(async move {
|
||||
format!("{} Lv{} {}\n{} {}",
|
||||
client.character.name,
|
||||
LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp),
|
||||
client.user.guildcard,
|
||||
client.character.char_class,
|
||||
client.area.unwrap_or(MapArea::Pioneer2Ep1))
|
||||
})).await
|
||||
})).await
|
||||
.into_iter()
|
||||
.collect::<Result<Vec<_>, ShipError>>()?
|
||||
.join("\n")
|
||||
};
|
||||
Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new(room_info)))])
|
||||
}
|
||||
|
@ -4,381 +4,452 @@ use crate::entity::gateway::EntityGateway;
|
||||
use crate::entity::item::Meseta;
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::common::leveltable::LEVEL_TABLE;
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation};
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients, ItemDropLocation};
|
||||
use crate::ship::room::Rooms;
|
||||
use crate::ship::location::{ClientLocation, ClientLocationError};
|
||||
use crate::ship::items::ClientItemId;
|
||||
use crate::ship::packet::builder;
|
||||
use crate::ship::items::state::ItemState;
|
||||
use crate::ship::items::tasks::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta};
|
||||
|
||||
pub async fn request_exp<EG: EntityGateway>(id: ClientId,
|
||||
request_exp: &RequestExp,
|
||||
mut entity_gateway: EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
rooms: &mut Rooms)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get_mut(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.as_mut()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
pub async fn request_exp<EG>(id: ClientId,
|
||||
request_exp: RequestExp,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
let monster = room.maps.enemy_by_id(request_exp.enemy_id as usize)?;
|
||||
let monster_stats = room.monster_stats.get(&monster.monster).ok_or(ShipError::UnknownMonster(monster.monster))?;
|
||||
let enemy_id = request_exp.enemy_id as usize;
|
||||
let enemy_exp = rooms.with(room_id, |room| Box::pin(async move {
|
||||
let monster = room.maps.enemy_by_id(enemy_id)?;
|
||||
let monster_stats = room.monster_stats.get(&monster.monster).ok_or_else(|| ShipError::UnknownMonster(monster.monster))?;
|
||||
Ok::<_, ShipError>(monster_stats.exp)
|
||||
})).await??;
|
||||
|
||||
let exp_gain = if request_exp.last_hitter == 1 {
|
||||
monster_stats.exp
|
||||
enemy_exp
|
||||
}
|
||||
else {
|
||||
((monster_stats.exp as f32) * 0.8) as u32
|
||||
((enemy_exp as f32) * 0.8) as u32
|
||||
};
|
||||
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let gain_exp_pkt = builder::message::character_gained_exp(area_client, exp_gain);
|
||||
let mut exp_pkts: Box<dyn Iterator<Item = _> + Send> = Box::new(clients_in_area.clone().into_iter()
|
||||
let mut exp_pkts: Vec<_> = clients_in_area.clone().into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::GiveCharacterExp(gain_exp_pkt.clone()))))
|
||||
}));
|
||||
})
|
||||
.collect();
|
||||
|
||||
let before_level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp);
|
||||
let after_level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp + exp_gain);
|
||||
let (char_class, exp) = clients.with(id, |client| Box::pin(async move {
|
||||
(client.character.char_class, client.character.exp)
|
||||
})).await?;
|
||||
|
||||
let before_level = LEVEL_TABLE.get_level_from_exp(char_class, exp);
|
||||
let after_level = LEVEL_TABLE.get_level_from_exp(char_class, exp + exp_gain);
|
||||
let level_up = before_level != after_level;
|
||||
|
||||
if level_up {
|
||||
let (_, before_stats) = LEVEL_TABLE.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||
let (after_level, after_stats) = LEVEL_TABLE.get_stats_from_exp(client.character.char_class, client.character.exp + exp_gain);
|
||||
let (_, before_stats) = LEVEL_TABLE.get_stats_from_exp(char_class, exp);
|
||||
let (after_level, after_stats) = LEVEL_TABLE.get_stats_from_exp(char_class, exp + exp_gain);
|
||||
|
||||
let level_up_pkt = builder::message::character_leveled_up(area_client, after_level, before_stats, after_stats);
|
||||
exp_pkts = Box::new(exp_pkts.chain(clients_in_area.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerLevelUp(level_up_pkt.clone()))))
|
||||
})))
|
||||
exp_pkts.extend(clients_in_area.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerLevelUp(level_up_pkt.clone()))))
|
||||
}));
|
||||
}
|
||||
|
||||
client.character.exp += exp_gain;
|
||||
entity_gateway.save_character(&client.character).await?;
|
||||
clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
Box::pin(async move {
|
||||
client.character.exp += exp_gain;
|
||||
entity_gateway.save_character(&client.character).await
|
||||
})}).await??;
|
||||
|
||||
Ok(exp_pkts)
|
||||
}
|
||||
|
||||
pub async fn player_drop_item<EG>(id: ClientId,
|
||||
player_drop_item: &PlayerDropItem,
|
||||
mut entity_gateway: EG,
|
||||
player_drop_item: PlayerDropItem,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
rooms: &mut Rooms,
|
||||
clients: &Clients,
|
||||
rooms: &Rooms,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get_mut(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.as_mut()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
let area = room.map_areas.get_area_map(player_drop_item.map_area)?;
|
||||
drop_item(item_state, &mut entity_gateway, &client.character, &ClientItemId(player_drop_item.item_id), *area, (player_drop_item.x, player_drop_item.y, player_drop_item.z)).await?;
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let map_area = rooms.with(room_id, |room| Box::pin(async move {
|
||||
room.map_areas.get_area_map(player_drop_item.map_area)
|
||||
})).await??;
|
||||
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
drop_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(player_drop_item.item_id), map_area, (player_drop_item.x, player_drop_item.y, player_drop_item.z)).await
|
||||
})}).await??;
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let pdi = player_drop_item.clone();
|
||||
Ok(Box::new(clients_in_area.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerDropItem(pdi.clone()))))
|
||||
})))
|
||||
Ok(clients_in_area.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerDropItem(pdi.clone()))))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn drop_coordinates(id: ClientId,
|
||||
drop_coordinates: &DropCoordinates,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
rooms: &Rooms)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
pub async fn drop_coordinates(id: ClientId,
|
||||
drop_coordinates: DropCoordinates,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.as_ref()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let map_area = rooms.with(room_id, |room| Box::pin(async move {
|
||||
room.map_areas.get_area_map(drop_coordinates.map_area)
|
||||
})).await??;
|
||||
|
||||
client.item_drop_location = Some(ItemDropLocation {
|
||||
map_area: *room.map_areas.get_area_map(drop_coordinates.map_area)?,
|
||||
x: drop_coordinates.x,
|
||||
z: drop_coordinates.z,
|
||||
item_id: ClientItemId(drop_coordinates.item_id),
|
||||
});
|
||||
clients.with_mut(id, |client| Box::pin(async move {
|
||||
client.item_drop_location = Some(ItemDropLocation {
|
||||
map_area,
|
||||
x: drop_coordinates.x,
|
||||
z: drop_coordinates.z,
|
||||
item_id: ClientItemId(drop_coordinates.item_id),
|
||||
});
|
||||
})).await?;
|
||||
|
||||
Ok(Box::new(None.into_iter())) // TODO: do we need to send a packet here?
|
||||
Ok(Vec::new()) // TODO: do we need to send a packet here?
|
||||
}
|
||||
|
||||
pub async fn no_longer_has_item<EG>(id: ClientId,
|
||||
no_longer_has_item: &PlayerNoLongerHasItem,
|
||||
mut entity_gateway: EG,
|
||||
no_longer_has_item: PlayerNoLongerHasItem,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
if let Some(drop_location) = client.item_drop_location {
|
||||
//let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
let (drop_location, tek) = clients.with(id, |client| Box::pin(async move {
|
||||
(client.item_drop_location, client.tek)
|
||||
})).await?;
|
||||
if let Some(drop_location) = drop_location {
|
||||
if drop_location.item_id.0 != no_longer_has_item.item_id {
|
||||
return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into());
|
||||
return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id));
|
||||
}
|
||||
|
||||
if no_longer_has_item.item_id == 0xFFFFFFFF {
|
||||
let dropped_meseta = drop_meseta(item_state, &mut entity_gateway, &client.character, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await?;
|
||||
let dropped_meseta = clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
client.item_drop_location = None;
|
||||
drop_meseta(&mut item_state, &mut entity_gateway, &client.character, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await
|
||||
})}).await??;
|
||||
|
||||
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?;
|
||||
let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount as u32);
|
||||
client.item_drop_location = None;
|
||||
let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount);
|
||||
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(Box::new(clients_in_area.into_iter()
|
||||
.flat_map(move |c| {
|
||||
std::iter::once((c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_meseta_pkt.clone())))))
|
||||
.chain(
|
||||
if c.client != id {
|
||||
Box::new(std::iter::once(
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(no_longer_has_meseta_pkt.clone()))))
|
||||
)) as Box<dyn Iterator<Item = _> + Send>
|
||||
}
|
||||
else {
|
||||
Box::new(std::iter::empty()) as Box<dyn Iterator<Item = _> + Send>
|
||||
}
|
||||
)
|
||||
})
|
||||
))
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(clients_in_area.into_iter()
|
||||
.flat_map(move |c| {
|
||||
std::iter::once((c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_meseta_pkt.clone())))))
|
||||
.chain(
|
||||
if c.client != id {
|
||||
Box::new(std::iter::once(
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(no_longer_has_meseta_pkt.clone()))))
|
||||
)) as Box<dyn Iterator<Item = _> + Send>
|
||||
}
|
||||
else {
|
||||
Box::new(std::iter::empty()) as Box<dyn Iterator<Item = _> + Send>
|
||||
}
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
else {
|
||||
let dropped_item = drop_partial_item(item_state, &mut entity_gateway, &client.character, &drop_location.item_id, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await?;
|
||||
|
||||
let dropped_item = clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
client.item_drop_location = None;
|
||||
drop_partial_item(&mut item_state,
|
||||
&mut entity_gateway,
|
||||
&client.character,
|
||||
&drop_location.item_id,
|
||||
drop_location.map_area,
|
||||
(drop_location.x, drop_location.z),
|
||||
no_longer_has_item.amount)
|
||||
.await
|
||||
})}).await??;
|
||||
let dropped_item_pkt = builder::message::drop_split_stack(area_client, &dropped_item)?;
|
||||
client.item_drop_location = None;
|
||||
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(Box::new(clients_in_area.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_item_pkt.clone()))))
|
||||
})))
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(clients_in_area.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_item_pkt.clone()))))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
else if let Some(_tek) = client.tek {
|
||||
let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
else if let Some(_tek) = tek {
|
||||
let neighbors = client_location.get_client_neighbors(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let no_longer_has_item = no_longer_has_item.clone();
|
||||
Ok(Box::new(neighbors.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(no_longer_has_item.clone()))))
|
||||
})))
|
||||
Ok(neighbors.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(no_longer_has_item.clone()))))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
else {
|
||||
Err(ShipError::InvalidItem(ClientItemId(no_longer_has_item.item_id)).into())
|
||||
Err(ShipError::InvalidItem(ClientItemId(no_longer_has_item.item_id)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_player_position(id: ClientId,
|
||||
message: &Message,
|
||||
clients: &mut Clients,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
if let Ok(room_id) = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() }) {
|
||||
let room = rooms.get(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.as_ref()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
|
||||
match &message.msg {
|
||||
GameMessage::PlayerChangedMap(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerChangedMap2(p) => {
|
||||
client.area = room.map_areas.get_area_map(p.map_area).ok().cloned();
|
||||
},
|
||||
GameMessage::TellOtherPlayerMyLocation(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
client.z = p.z;
|
||||
client.area = room.map_areas.get_area_map(p.map_area).ok().cloned();
|
||||
},
|
||||
GameMessage::PlayerWarpingToFloor(p) => {
|
||||
client.area = room.map_areas.get_area_map(p.area as u16).ok().cloned();
|
||||
},
|
||||
GameMessage::PlayerTeleported(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerStopped(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerLoadedIn(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerWalking(p) => {
|
||||
client.x = p.x;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerRunning(p) => {
|
||||
client.x = p.x;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerWarped(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
},
|
||||
// GameMessage::PlayerChangedFloor(p) => {client.area = MapArea::from_value(&room.mode.episode(), p.map).ok();},
|
||||
GameMessage::InitializeSpeechNpc(p) => {
|
||||
client.x = p.x;
|
||||
client.z = p.z;
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
} else {}
|
||||
let m = message.clone();
|
||||
Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(m.clone()))
|
||||
})))
|
||||
pub async fn update_player_position(id: ClientId,
|
||||
message: Message,
|
||||
clients: &Clients,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
if let Ok(room_id) = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() }) {
|
||||
let msg = message.msg.clone();
|
||||
clients.with_mut(id, |client| {
|
||||
let rooms = rooms.clone();
|
||||
Box::pin(async move {
|
||||
match msg {
|
||||
GameMessage::PlayerChangedMap(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerChangedMap2(p) => {
|
||||
client.area = rooms.with(room_id, |room| Box::pin(async move {
|
||||
room.map_areas.get_area_map(p.map_area).ok()
|
||||
})).await?;
|
||||
},
|
||||
GameMessage::TellOtherPlayerMyLocation(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
client.z = p.z;
|
||||
client.area = rooms.with(room_id, |room| Box::pin(async move {
|
||||
room.map_areas.get_area_map(p.map_area).ok()
|
||||
})).await?;
|
||||
},
|
||||
GameMessage::PlayerWarpingToFloor(p) => {
|
||||
client.area = rooms.with(room_id, |room| Box::pin(async move {
|
||||
room.map_areas.get_area_map(p.area as u16).ok()
|
||||
})).await?;
|
||||
},
|
||||
GameMessage::PlayerTeleported(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerStopped(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerLoadedIn(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerWalking(p) => {
|
||||
client.x = p.x;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerRunning(p) => {
|
||||
client.x = p.x;
|
||||
client.z = p.z;
|
||||
},
|
||||
GameMessage::PlayerWarped(p) => {
|
||||
client.x = p.x;
|
||||
client.y = p.y;
|
||||
},
|
||||
// GameMessage::PlayerChangedFloor(p) => {client.area = MapArea::from_value(&room.mode.episode(), p.map).ok();},
|
||||
GameMessage::InitializeSpeechNpc(p) => {
|
||||
client.x = p.x;
|
||||
client.z = p.z;
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
Ok::<_, ShipError>(())
|
||||
})}).await??;
|
||||
}
|
||||
Ok(client_location.get_client_neighbors(id).await?.into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(message.clone()))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn charge_attack<EG>(id: ClientId,
|
||||
charge: &ChargeAttack,
|
||||
mut entity_gateway: EG,
|
||||
charge: ChargeAttack,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let meseta = charge.meseta;
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
// TODO: should probably validate this to be a legit number, I'd just hardcode 200 but vjaya
|
||||
take_meseta(&mut item_state, &mut entity_gateway, &client.character.id, Meseta(meseta)).await
|
||||
})}).await??;
|
||||
|
||||
// TODO: should probably validate this to be a legit number, I'd just hardcode 200 but vjaya
|
||||
take_meseta(item_state, &mut entity_gateway, &client.character.id, Meseta(charge.meseta)).await?;
|
||||
|
||||
let charge = charge.clone();
|
||||
Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(Message::new(GameMessage::ChargeAttack(charge.clone()))))
|
||||
})))
|
||||
Ok(client_location.get_client_neighbors(id).await.unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(Message::new(GameMessage::ChargeAttack(charge.clone()))))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn player_uses_item<EG>(id: ClientId,
|
||||
player_use_tool: &PlayerUseItem,
|
||||
mut entity_gateway: EG,
|
||||
_client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
player_use_tool: PlayerUseItem,
|
||||
entity_gateway: &mut EG,
|
||||
_client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
use_item(item_state, &mut entity_gateway, &mut client.character, &ClientItemId(player_use_tool.item_id), 1).await?;
|
||||
Ok(Box::new(None.into_iter())) // TODO: should probably tell other players we used an item
|
||||
clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
use_item(&mut item_state, &mut entity_gateway, &mut client.character, &ClientItemId(player_use_tool.item_id), 1).await
|
||||
})}).await??;
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
pub async fn player_used_medical_center<EG>(id: ClientId,
|
||||
pumc: &PlayerUsedMedicalCenter,
|
||||
mut entity_gateway: EG,
|
||||
pumc: PlayerUsedMedicalCenter,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
|
||||
take_meseta(item_state, &mut entity_gateway, &client.character.id, Meseta(10)).await?;
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
take_meseta(&mut item_state, &mut entity_gateway, &client.character.id, Meseta(10)).await
|
||||
})}).await??;
|
||||
|
||||
let pumc = pumc.clone();
|
||||
Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerUsedMedicalCenter(pumc.clone()))))
|
||||
})))
|
||||
Ok(client_location.get_client_neighbors(id).await.unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerUsedMedicalCenter(pumc.clone()))))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
|
||||
pub async fn player_feed_mag<EG>(id: ClientId,
|
||||
mag_feed: &PlayerFeedMag,
|
||||
mut entity_gateway: EG,
|
||||
mag_feed: PlayerFeedMag,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
feed_mag(item_state, &mut entity_gateway, &client.character, &ClientItemId(mag_feed.mag_id), &ClientItemId(mag_feed.item_id)).await?;
|
||||
|
||||
let mag_feed = mag_feed.clone();
|
||||
Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerFeedMag(mag_feed.clone()))))
|
||||
})))
|
||||
let cmag_feed = mag_feed.clone();
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
feed_mag(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(cmag_feed.mag_id), &ClientItemId(cmag_feed.item_id)).await
|
||||
})}).await??;
|
||||
|
||||
Ok(client_location.get_client_neighbors(id).await.unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerFeedMag(mag_feed.clone()))))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn player_equips_item<EG>(id: ClientId,
|
||||
pkt: &PlayerEquipItem,
|
||||
mut entity_gateway: EG,
|
||||
pkt: PlayerEquipItem,
|
||||
entity_gateway: &mut EG,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let equip_slot = if pkt.sub_menu > 0 {
|
||||
((pkt.sub_menu & 0x7) - 1) % 4
|
||||
}
|
||||
else {
|
||||
0
|
||||
};
|
||||
equip_item(item_state, &mut entity_gateway, &client.character, &ClientItemId(pkt.item_id), equip_slot).await?;
|
||||
Ok(Box::new(None.into_iter())) // TODO: tell other players you equipped an item
|
||||
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
equip_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(pkt.item_id), equip_slot).await
|
||||
})}).await??;
|
||||
Ok(Vec::new()) // TODO: tell other players you equipped an item
|
||||
}
|
||||
|
||||
pub async fn player_unequips_item<EG>(id: ClientId,
|
||||
pkt: &PlayerUnequipItem,
|
||||
mut entity_gateway: EG,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
pkt: PlayerUnequipItem,
|
||||
entity_gateway: &mut EG,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
unequip_item(item_state, &mut entity_gateway, &client.character, &ClientItemId(pkt.item_id)).await?;
|
||||
Ok(Box::new(None.into_iter())) // TODO: tell other players if you unequip an item
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
unequip_item(&mut item_state, &mut entity_gateway, &client.character, &ClientItemId(pkt.item_id)).await
|
||||
})}).await??;
|
||||
Ok(Vec::new()) // TODO: tell other players if you unequip an item
|
||||
}
|
||||
|
||||
pub async fn player_sorts_items<EG>(id: ClientId,
|
||||
pkt: &SortItems,
|
||||
mut entity_gateway: EG,
|
||||
pkt: SortItems,
|
||||
entity_gateway: &mut EG,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let item_ids = pkt.item_ids
|
||||
.iter()
|
||||
.filter_map(|item_id| {
|
||||
@ -390,21 +461,30 @@ where
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
sort_inventory(item_state, &mut entity_gateway, &client.character, item_ids).await?;
|
||||
Ok(Box::new(None.into_iter())) // TODO: clients probably care about each others item orders
|
||||
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
sort_inventory(&mut item_state, &mut entity_gateway, &client.character, item_ids).await
|
||||
})}).await??;
|
||||
Ok(Vec::new()) // TODO: clients probably care about each others item orders
|
||||
}
|
||||
|
||||
pub async fn player_sells_item<EG> (id: ClientId,
|
||||
sold_item: &PlayerSoldItem,
|
||||
mut entity_gateway: EG,
|
||||
clients: &mut Clients,
|
||||
sold_item: PlayerSoldItem,
|
||||
entity_gateway: &mut EG,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
sell_item(item_state, &mut entity_gateway, &client.character, ClientItemId(sold_item.item_id), sold_item.amount as u32).await?;
|
||||
// TODO: send the packet to other clients
|
||||
Ok(Box::new(None.into_iter()))
|
||||
clients.with(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
sell_item(&mut item_state, &mut entity_gateway, &client.character, ClientItemId(sold_item.item_id), sold_item.amount as u32).await
|
||||
})}).await??;
|
||||
Ok(Vec::new()) // TODO: send the packet to other clients
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom};
|
||||
use futures::stream::{FuturesOrdered, StreamExt};
|
||||
use libpso::packet::ship::*;
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms};
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
||||
use crate::ship::room::Rooms;
|
||||
use crate::ship::location::{ClientLocation, ClientLocationError};
|
||||
use crate::ship::packet::builder::quest;
|
||||
use libpso::util::array_to_utf8;
|
||||
|
||||
// TOOD: enum
|
||||
enum QuestFileType {
|
||||
Bin,
|
||||
Dat
|
||||
@ -36,165 +37,205 @@ fn parse_filename(filename_bytes: &[u8; 16]) -> Result<(u16, u16, QuestFileType)
|
||||
}
|
||||
|
||||
|
||||
pub fn send_quest_category_list(id: ClientId, rql: &RequestQuestList, client_location: &ClientLocation, rooms: &mut Rooms) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get_mut(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
let qcl = quest::quest_category_list(&room.quests[rql.flag.clamp(0, (room.quests.len() - 1) as u32) as usize]);
|
||||
room.set_quest_group(rql.flag as usize);
|
||||
Ok(Box::new(vec![(id, SendShipPacket::QuestCategoryList(qcl))].into_iter()))
|
||||
pub async fn send_quest_category_list(id: ClientId,
|
||||
rql: RequestQuestList,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let rql = rql.clone();
|
||||
rooms.with_mut(room_id, |room| Box::pin(async move {
|
||||
let qcl = quest::quest_category_list(&room.quests[rql.flag.clamp(0, (room.quests.len() - 1) as u32) as usize]);
|
||||
room.set_quest_group(rql.flag as usize);
|
||||
Ok(vec![(id, SendShipPacket::QuestCategoryList(qcl))])
|
||||
})).await?
|
||||
}
|
||||
|
||||
pub fn select_quest_category(id: ClientId, menuselect: &MenuSelect, client_location: &ClientLocation, rooms: &mut Rooms) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get_mut(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(menuselect.item as usize)
|
||||
.ok_or(ShipError::InvalidQuestCategory(menuselect.item))?;
|
||||
pub async fn select_quest_category(id: ClientId,
|
||||
menuselect: MenuSelect,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
rooms.with(room_id, |room| Box::pin(async move {
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(menuselect.item as usize)
|
||||
.ok_or_else(|| ShipError::InvalidQuestCategory(menuselect.item as u16))?;
|
||||
|
||||
let ql = quest::quest_list(menuselect.item, category_quests);
|
||||
for q in ql.quests.clone() {
|
||||
println!("name: {:?} quest_id: {}", q.name, q.quest_id);
|
||||
}
|
||||
Ok(Box::new(vec![(id, SendShipPacket::QuestOptionList(ql))].into_iter()))
|
||||
let ql = quest::quest_list(menuselect.item, category_quests);
|
||||
Ok(vec![(id, SendShipPacket::QuestOptionList(ql))])
|
||||
})).await?
|
||||
}
|
||||
|
||||
|
||||
pub fn quest_detail(id: ClientId, questdetailrequest: &QuestDetailRequest, client_location: &ClientLocation, rooms: &mut Rooms) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get_mut(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(questdetailrequest.category as usize)
|
||||
.ok_or(ShipError::InvalidQuestCategory(questdetailrequest.category as u32))?;
|
||||
pub async fn quest_detail(id: ClientId,
|
||||
questdetailrequest: QuestDetailRequest,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
rooms.with(room_id, |room| Box::pin(async move {
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(questdetailrequest.category as usize)
|
||||
.ok_or_else(|| ShipError::InvalidQuestCategory(questdetailrequest.category))?;
|
||||
|
||||
let quest = category_quests.iter()
|
||||
.find(|q| {
|
||||
q.id == questdetailrequest.quest as u16
|
||||
}).ok_or(ShipError::InvalidQuest(questdetailrequest.quest as u32))?;
|
||||
let quest = category_quests.iter()
|
||||
.find(|q| {
|
||||
q.id == questdetailrequest.quest
|
||||
}).ok_or_else(|| ShipError::InvalidQuest(questdetailrequest.quest))?;
|
||||
|
||||
let qd = quest::quest_detail(quest);
|
||||
|
||||
Ok(Box::new(vec![(id, SendShipPacket::QuestDetail(qd))].into_iter()))
|
||||
let qd = quest::quest_detail(quest);
|
||||
|
||||
Ok(vec![(id, SendShipPacket::QuestDetail(qd))])
|
||||
})).await?
|
||||
}
|
||||
|
||||
pub fn player_chose_quest(id: ClientId, questmenuselect: &QuestMenuSelect, clients: &mut Clients, client_location: &ClientLocation, rooms: &mut Rooms)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get_mut(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(questmenuselect.category as usize)
|
||||
.ok_or(ShipError::InvalidQuestCategory(questmenuselect.category as u32))?;
|
||||
pub async fn player_chose_quest(id: ClientId,
|
||||
questmenuselect: QuestMenuSelect,
|
||||
clients: &Clients,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
let quest = category_quests.iter()
|
||||
.find(|q| {
|
||||
q.id == questmenuselect.quest as u16
|
||||
}).ok_or(ShipError::InvalidQuest(questmenuselect.quest as u32))?;
|
||||
let client_location = client_location.clone();
|
||||
let questmenuselect = questmenuselect.clone();
|
||||
rooms.with_mut(room_id, |room| {
|
||||
let clients = clients.clone();
|
||||
Box::pin(async move {
|
||||
let quest = room.quests[room.quest_group.value()].iter()
|
||||
.nth(questmenuselect.category as usize)
|
||||
.ok_or_else(|| ShipError::InvalidQuestCategory(questmenuselect.category))?
|
||||
.1
|
||||
.iter()
|
||||
.find(|q| {
|
||||
q.id == questmenuselect.quest
|
||||
})
|
||||
.ok_or_else(|| ShipError::InvalidQuest(questmenuselect.quest))?
|
||||
.clone();
|
||||
|
||||
room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &room.rare_monster_table);
|
||||
room.map_areas = quest.map_areas.clone();
|
||||
let rare_monster_drops = room.rare_monster_table.clone();
|
||||
room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &rare_monster_drops);
|
||||
room.map_areas = quest.map_areas.clone();
|
||||
|
||||
let bin = quest::quest_header(questmenuselect, &quest.bin_blob, "bin");
|
||||
let dat = quest::quest_header(questmenuselect, &quest.dat_blob, "dat");
|
||||
let bin = quest::quest_header(&questmenuselect, &quest.bin_blob, "bin");
|
||||
let dat = quest::quest_header(&questmenuselect, &quest.dat_blob, "dat");
|
||||
|
||||
let area_clients = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
area_clients.iter().for_each(|c| {
|
||||
if let Some(client) = clients.get_mut(&c.client) {
|
||||
client.done_loading_quest = false;
|
||||
let area_clients = client_location.get_all_clients_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
for client in &area_clients {
|
||||
clients.with_mut(client.client, |client| Box::pin(async move {
|
||||
client.done_loading_quest = false;
|
||||
})).await?;
|
||||
}
|
||||
Ok(area_clients
|
||||
.into_iter()
|
||||
.flat_map(move |c| {
|
||||
vec![(c.client, SendShipPacket::QuestHeader(bin.clone())), (c.client, SendShipPacket::QuestHeader(dat.clone()))]
|
||||
})
|
||||
.collect())
|
||||
})}).await?
|
||||
}
|
||||
|
||||
pub async fn quest_file_request(id: ClientId,
|
||||
quest_file_request: QuestFileRequest,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &mut Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
{
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
let quest_file_request = quest_file_request.clone();
|
||||
rooms.with(room_id, |room| Box::pin(async move {
|
||||
let (category_id, quest_id, datatype) = parse_filename(&quest_file_request.filename)?;
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(category_id as usize)
|
||||
.ok_or_else(|| ShipError::InvalidQuestCategory(category_id))?;
|
||||
|
||||
let quest = category_quests.iter()
|
||||
.find(|q| {
|
||||
q.id == quest_id
|
||||
}).ok_or_else(|| ShipError::InvalidQuest(quest_id))?;
|
||||
|
||||
let blob = match datatype {
|
||||
QuestFileType::Bin => &quest.bin_blob,
|
||||
QuestFileType::Dat => &quest.dat_blob,
|
||||
};
|
||||
let mut blob_cursor = Cursor::new(&**blob);
|
||||
|
||||
let mut subblob = [0u8; 0x400];
|
||||
let blob_length = blob_cursor.read(&mut subblob)?;
|
||||
let qc = quest::quest_chunk(0, quest_file_request.filename, subblob, blob_length);
|
||||
|
||||
Ok(vec![(id, SendShipPacket::QuestChunk(qc))])
|
||||
})).await?
|
||||
}
|
||||
|
||||
pub async fn quest_chunk_ack(id: ClientId,
|
||||
quest_chunk_ack: QuestChunkAck,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
let quest_chunk_ack = quest_chunk_ack.clone();
|
||||
rooms.with(room_id, |room| Box::pin(async move {
|
||||
let (category_id, quest_id, datatype) = parse_filename(&quest_chunk_ack.filename)?;
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(category_id as usize)
|
||||
.ok_or_else(|| ShipError::InvalidQuestCategory(category_id))?;
|
||||
|
||||
let quest = category_quests.iter()
|
||||
.find(|q| {
|
||||
q.id == quest_id
|
||||
}).ok_or_else(|| ShipError::InvalidQuest(quest_id))?;
|
||||
|
||||
let blob = match datatype {
|
||||
QuestFileType::Bin => &quest.bin_blob,
|
||||
QuestFileType::Dat => &quest.dat_blob,
|
||||
};
|
||||
|
||||
let mut blob_cursor = Cursor::new(&**blob);
|
||||
blob_cursor.seek(SeekFrom::Start((quest_chunk_ack.chunk_num as u64 + 1) * 0x400))?;
|
||||
let mut subblob = [0u8; 0x400];
|
||||
let blob_length = blob_cursor.read(&mut subblob)?;
|
||||
if blob_length == 0 {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
});
|
||||
Ok(Box::new(area_clients.into_iter().flat_map(move |c| {
|
||||
vec![(c.client, SendShipPacket::QuestHeader(bin.clone())), (c.client, SendShipPacket::QuestHeader(dat.clone()))]
|
||||
})))
|
||||
let qc = quest::quest_chunk(quest_chunk_ack.chunk_num + 1, quest_chunk_ack.filename, subblob, blob_length);
|
||||
|
||||
Ok(vec![(id, SendShipPacket::QuestChunk(qc))])
|
||||
})).await?
|
||||
}
|
||||
|
||||
pub fn quest_file_request(id: ClientId, quest_file_request: &QuestFileRequest, client_location: &ClientLocation, rooms: &mut Rooms) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get_mut(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
pub async fn done_loading_quest(id: ClientId,
|
||||
clients: &Clients,
|
||||
client_location: &ClientLocation)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
clients.with_mut(id, |client| Box::pin(async move {
|
||||
client.done_loading_quest = true;
|
||||
})).await?;
|
||||
let area_clients = client_location.get_all_clients_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
let (category_id, quest_id, datatype) = parse_filename(&quest_file_request.filename)?;
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(category_id as usize)
|
||||
.ok_or(ShipError::InvalidQuestCategory(category_id as u32))?;
|
||||
|
||||
let quest = category_quests.iter()
|
||||
.find(|q| {
|
||||
q.id == quest_id as u16
|
||||
}).ok_or(ShipError::InvalidQuest(quest_id as u32))?;
|
||||
|
||||
let blob = match datatype {
|
||||
QuestFileType::Bin => &quest.bin_blob,
|
||||
QuestFileType::Dat => &quest.dat_blob,
|
||||
};
|
||||
let mut blob_cursor = Cursor::new(blob);
|
||||
|
||||
let mut subblob = [0u8; 0x400];
|
||||
let blob_length = blob_cursor.read(&mut subblob)?;
|
||||
let qc = quest::quest_chunk(0, quest_file_request.filename, subblob, blob_length);
|
||||
let all_loaded = area_clients.iter()
|
||||
.map(|client|
|
||||
clients.with(client.client, |client| Box::pin(async move {
|
||||
client.done_loading_quest
|
||||
}))
|
||||
)
|
||||
.collect::<FuturesOrdered<_>>()
|
||||
.all(|c| async move {
|
||||
c.unwrap_or(false)
|
||||
}).await;
|
||||
|
||||
Ok(Box::new(vec![(id, SendShipPacket::QuestChunk(qc))].into_iter()))
|
||||
}
|
||||
|
||||
pub fn quest_chunk_ack(id: ClientId, quest_chunk_ack: &QuestChunkAck, client_location: &ClientLocation, rooms: &mut Rooms) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let room = rooms.get_mut(room_id.0)
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?.as_mut()
|
||||
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
|
||||
let (category_id, quest_id, datatype) = parse_filename(&quest_chunk_ack.filename)?;
|
||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||
.nth(category_id as usize)
|
||||
.ok_or(ShipError::InvalidQuestCategory(category_id as u32))?;
|
||||
|
||||
let quest = category_quests.iter()
|
||||
.find(|q| {
|
||||
q.id == quest_id
|
||||
}).ok_or(ShipError::InvalidQuest(quest_id as u32))?;
|
||||
|
||||
let blob = match datatype {
|
||||
QuestFileType::Bin => &quest.bin_blob,
|
||||
QuestFileType::Dat => &quest.dat_blob,
|
||||
};
|
||||
|
||||
let mut blob_cursor = Cursor::new(blob);
|
||||
blob_cursor.seek(SeekFrom::Start((quest_chunk_ack.chunk_num as u64 + 1) * 0x400))?;
|
||||
let mut subblob = [0u8; 0x400];
|
||||
let blob_length = blob_cursor.read(&mut subblob)?;
|
||||
if blob_length == 0 {
|
||||
return Ok(Box::new(None.into_iter()));
|
||||
}
|
||||
let qc = quest::quest_chunk(quest_chunk_ack.chunk_num + 1, quest_chunk_ack.filename, subblob, blob_length);
|
||||
|
||||
Ok(Box::new(vec![(id, SendShipPacket::QuestChunk(qc))].into_iter()))
|
||||
}
|
||||
|
||||
pub fn done_loading_quest(id: ClientId, clients: &mut Clients, client_location: &ClientLocation)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
client.done_loading_quest = true;
|
||||
let area_clients = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let all_loaded = area_clients.iter().all(|c| {
|
||||
clients.get(&c.client)
|
||||
.map(|client| {
|
||||
client.done_loading_quest
|
||||
})
|
||||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
if all_loaded {
|
||||
Ok(Box::new(area_clients.into_iter().map(|c| {
|
||||
(c.client, SendShipPacket::DoneLoadingQuest(DoneLoadingQuest {}))
|
||||
})))
|
||||
Ok(area_clients
|
||||
.iter()
|
||||
.map(|c| {
|
||||
(c.client, SendShipPacket::DoneLoadingQuest(DoneLoadingQuest {}))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
else {
|
||||
Ok(Box::new(None.into_iter()))
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
|
@ -1,203 +1,218 @@
|
||||
use std::convert::{TryFrom, Into};
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
use libpso::packet::ship::*;
|
||||
use libpso::packet::messages::*;
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::common::leveltable::LEVEL_TABLE;
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients};
|
||||
use crate::ship::location::{ClientLocation, RoomId, RoomLobby, ClientLocationError};
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
||||
use crate::ship::room::Rooms;
|
||||
use crate::ship::location::{ClientLocation, RoomId, RoomLobby, GetAreaError};
|
||||
use crate::ship::packet::builder;
|
||||
use crate::ship::room;
|
||||
use crate::ship::items::state::ItemState;
|
||||
use std::convert::{TryFrom};
|
||||
|
||||
pub fn create_room(id: ClientId,
|
||||
create_room: &CreateRoom,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_state: &mut ItemState,
|
||||
rooms: &mut Rooms)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp);
|
||||
pub async fn create_room(id: ClientId,
|
||||
create_room: CreateRoom,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let level = clients.with(id, |client| Box::pin(async move {
|
||||
LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp)
|
||||
})).await?;
|
||||
match room::Difficulty::try_from(create_room.difficulty)? {
|
||||
room::Difficulty::Ultimate => {
|
||||
if level < 80 {
|
||||
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 80 \nto create Ultimate rooms.".into())))].into_iter()))
|
||||
}
|
||||
room::Difficulty::Ultimate if level < 80 => {
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 80 \nto create Ultimate rooms.".into())))])
|
||||
},
|
||||
room::Difficulty::VeryHard => {
|
||||
if level < 40 {
|
||||
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 40 \nto create Very Hard rooms.".into())))].into_iter()))
|
||||
}
|
||||
room::Difficulty::VeryHard if level < 40 => {
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 40 \nto create Very Hard rooms.".into())))])
|
||||
},
|
||||
room::Difficulty::Hard => {
|
||||
if level < 20 {
|
||||
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 20 \nto create Hard rooms.".into())))].into_iter()))
|
||||
}
|
||||
room::Difficulty::Hard if level < 20 => {
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 20 \nto create Hard rooms.".into())))])
|
||||
},
|
||||
room::Difficulty::Normal => {},
|
||||
_ => {},
|
||||
};
|
||||
|
||||
let area = client_location.get_area(id).unwrap();
|
||||
let area_client = client_location.get_local_client(id).unwrap();
|
||||
let lobby_neighbors = client_location.get_client_neighbors(id).unwrap();
|
||||
let room_id = client_location.create_new_room(id).unwrap();
|
||||
let mut room = room::RoomState::from_create_room(create_room, client.character.section_id).unwrap();
|
||||
room.bursting = true;
|
||||
let area = client_location.get_area(id).await?;
|
||||
let area_client = client_location.get_local_client(id).await?;
|
||||
let lobby_neighbors = client_location.get_client_neighbors(id).await?;
|
||||
|
||||
item_state.add_character_to_room(room_id, &client.character, area_client);
|
||||
let room_id = client_location.create_new_room(id).await?;
|
||||
let room = clients.with(id, |client| {
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
item_state.add_character_to_room(room_id, &client.character, area_client).await;
|
||||
let mut room = room::RoomState::from_create_room(&create_room, client.character.section_id)?;
|
||||
room.bursting = true;
|
||||
Ok::<_, ShipError>(room)
|
||||
})}).await??;
|
||||
|
||||
let join_room = builder::room::join_room(id, clients, client_location, room_id, &room)?;
|
||||
rooms[room_id.0] = Some(room);
|
||||
let join_room = builder::room::join_room(id, clients, client_location, room_id, &room).await?;
|
||||
rooms.add(room_id, room).await?;
|
||||
|
||||
let mut result: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = Box::new(
|
||||
vec![(id, SendShipPacket::JoinRoom(join_room))].into_iter()
|
||||
);
|
||||
if let Ok(leader) = client_location.get_area_leader(area) {
|
||||
let mut result = vec![(id, SendShipPacket::JoinRoom(join_room))];
|
||||
if let Ok(leader) = client_location.get_area_leader(area).await {
|
||||
let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()));
|
||||
result = Box::new(result.chain(lobby_neighbors
|
||||
.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, leave_lobby.clone())
|
||||
})));
|
||||
result.extend(lobby_neighbors
|
||||
.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, leave_lobby.clone())
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn room_name_request(id: ClientId,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
let area = client_location.get_area(id).unwrap();
|
||||
pub async fn room_name_request(id: ClientId,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let area = client_location.get_area(id).await?;
|
||||
match area {
|
||||
RoomLobby::Room(room) => Box::new(vec![(id, SendShipPacket::RoomNameResponse(RoomNameResponse {name: rooms[room.0].as_ref().unwrap().name.clone()}))].into_iter()),
|
||||
RoomLobby::Lobby(_) => panic!()
|
||||
RoomLobby::Room(room) => {
|
||||
rooms.with(room, |room| Box::pin(async move {
|
||||
vec![(id, SendShipPacket::RoomNameResponse(RoomNameResponse {
|
||||
name: room.name.clone()
|
||||
}))]
|
||||
})).await
|
||||
},
|
||||
RoomLobby::Lobby(_) => Err(GetAreaError::NotInRoom.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join_room(id: ClientId,
|
||||
pkt: &MenuSelect,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_state: &mut ItemState,
|
||||
rooms: &mut Rooms)
|
||||
-> Result<Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let level = LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp);
|
||||
// let room = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))?.as_ref().unwrap(); // clippy look what you made me do
|
||||
if let Some(room) = rooms.get(pkt.item as usize).ok_or(ShipError::InvalidRoom(pkt.item))? {
|
||||
pub async fn join_room(id: ClientId,
|
||||
pkt: MenuSelect,
|
||||
client_location: &mut ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let room_id = RoomId(pkt.item as usize);
|
||||
if !rooms.exists(room_id).await {
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("This room no longer exists!".into())))])
|
||||
}
|
||||
let level = clients.with(id, |client| Box::pin(async move {
|
||||
LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp)
|
||||
})).await?;
|
||||
let (difficulty, bursting) = rooms.with(room_id, |room| Box::pin(async move {
|
||||
(room.mode.difficulty(), room.bursting)
|
||||
})).await?;
|
||||
|
||||
match room.mode.difficulty() {
|
||||
room::Difficulty::Ultimate => {
|
||||
if level < 80 {
|
||||
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 80 \nto join Ultimate rooms.".into())))].into_iter()))
|
||||
}
|
||||
},
|
||||
room::Difficulty::VeryHard => {
|
||||
if level < 40 {
|
||||
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 40 \nto join Very Hard rooms.".into())))].into_iter()))
|
||||
}
|
||||
},
|
||||
room::Difficulty::Hard => {
|
||||
if level < 20 {
|
||||
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 20 \nto join Hard rooms.".into())))].into_iter()))
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
match difficulty {
|
||||
room::Difficulty::Ultimate if level < 80 => {
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 80 \nto join Ultimate rooms.".into())))])
|
||||
},
|
||||
room::Difficulty::VeryHard if level < 40 => {
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 40 \nto join Very Hard rooms.".into())))])
|
||||
},
|
||||
room::Difficulty::Hard if level < 20 => {
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 20 \nto join Hard rooms.".into())))])
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
let original_area = client_location.get_area(id).unwrap();
|
||||
let original_neighbors = client_location.get_client_neighbors(id).unwrap();
|
||||
if room.bursting {
|
||||
return Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("player is bursting\nplease wait".into())))].into_iter()))
|
||||
}
|
||||
let room_id = RoomId(pkt.item as usize);
|
||||
let original_room_clients = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
client_location.add_client_to_room(id, room_id).unwrap(); // TODO: show room full error or whatever
|
||||
if bursting {
|
||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("player is bursting\nplease wait".into())))])
|
||||
}
|
||||
|
||||
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() })?;
|
||||
let original_area = client_location.get_area(id).await?;
|
||||
let original_neighbors = client_location.get_client_neighbors(id).await?;
|
||||
let original_room_clients = client_location.get_clients_in_room(room_id).await?;
|
||||
client_location.add_client_to_room(id, room_id).await?;
|
||||
let area_client = client_location.get_local_client(id).await?;
|
||||
let room_leader = client_location.get_room_leader(room_id).await?;
|
||||
|
||||
item_state.add_character_to_room(room_id, &client.character, area_client);
|
||||
clients.with(id, |client| {
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
item_state.add_character_to_room(room_id, &client.character, area_client).await;
|
||||
})}).await?;
|
||||
|
||||
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_state, room_id)?;
|
||||
let join_room = rooms.with(room_id, |room| {
|
||||
let clients = clients.clone();
|
||||
let client_location = client_location.clone();
|
||||
Box::pin(async move {
|
||||
builder::room::join_room(id, &clients, &client_location, room_id, room).await
|
||||
})}).await??;
|
||||
let add_to = clients.with(id, |client| {
|
||||
let item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
builder::room::add_to_room(id, client, &area_client, &room_leader, &item_state, room_id).await
|
||||
})}).await??;
|
||||
|
||||
let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap();
|
||||
rooms.with_mut(room_id, |room| Box::pin(async move {
|
||||
room.bursting = true;
|
||||
|
||||
let mut result: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = Box::new(
|
||||
vec![(id, SendShipPacket::JoinRoom(join_room))]
|
||||
.into_iter()
|
||||
.chain(original_room_clients.into_iter()
|
||||
.map(move |c| (c.client, SendShipPacket::AddToRoom(add_to.clone())))
|
||||
));
|
||||
|
||||
if let Ok(leader) = client_location.get_area_leader(original_area) {
|
||||
let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()));
|
||||
result = Box::new(result.chain(original_neighbors.into_iter()
|
||||
.map(move |c| (c.client, leave_lobby.clone()))))
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
} else {
|
||||
Ok(Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Game is no longer active".into())))].into_iter()))
|
||||
}
|
||||
})).await?;
|
||||
|
||||
Ok(vec![(id, SendShipPacket::JoinRoom(join_room))]
|
||||
.into_iter()
|
||||
.chain(original_room_clients.into_iter()
|
||||
.map(move |c| (c.client, SendShipPacket::AddToRoom(add_to.clone()))))
|
||||
.chain(futures::stream::iter(original_neighbors.into_iter())
|
||||
.filter_map(|c| {
|
||||
let client_location = client_location.clone();
|
||||
async move {
|
||||
client_location.get_area_leader(original_area).await.ok().map(|leader| {
|
||||
let leave_lobby = SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()));
|
||||
(c.client, leave_lobby)
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.await
|
||||
)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn done_bursting(id: ClientId,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &mut Rooms)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
let area = client_location.get_area(id).unwrap();
|
||||
let mut rare_monster_list: Option<Vec<u16>> = None;
|
||||
if let RoomLobby::Room(room_id) = area {
|
||||
if let Some(room) = rooms.get_mut(room_id.0).unwrap().as_mut() {
|
||||
room.bursting = false;
|
||||
rare_monster_list = Some(room.maps.get_rare_monster_list());
|
||||
};
|
||||
}
|
||||
let area_client = client_location.get_local_client(id).unwrap(); // TODO: unwrap
|
||||
let mut result: Box<dyn Iterator<Item=(ClientId, SendShipPacket)> + Send> = Box::new(
|
||||
client_location.get_client_neighbors(id).unwrap().into_iter() // TODO: unwrap
|
||||
.flat_map(move |client| {
|
||||
vec![
|
||||
(client.client, SendShipPacket::Message(Message::new(GameMessage::BurstDone(BurstDone {
|
||||
client: area_client.local_client.id(),
|
||||
target: 0
|
||||
})))),
|
||||
]
|
||||
})
|
||||
);
|
||||
pub async fn done_bursting(id: ClientId,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let room_id = client_location.get_room(id).await?;
|
||||
let rare_monster_list = rooms.with_mut(room_id, |room| Box::pin(async move {
|
||||
room.bursting = false;
|
||||
room.maps.get_rare_monster_list()
|
||||
})).await?;
|
||||
|
||||
// TODO: check how often `done_bursting` is called. ie: make sure it's only used when joining a room and not each time a player warps in a pipe
|
||||
if let Some(rare_list) = rare_monster_list {
|
||||
let rare_monster_packet = SendShipPacket::RareMonsterList(builder::room::build_rare_monster_list(rare_list));
|
||||
result = Box::new(result.chain(vec![(id, rare_monster_packet)])); // TODO: make sure we arent clobbering `result` here
|
||||
}
|
||||
|
||||
result
|
||||
let area_client = client_location.get_local_client(id).await?;
|
||||
Ok(client_location.get_client_neighbors(id).await?.into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(Message::new(GameMessage::BurstDone(BurstDone {
|
||||
client: area_client.local_client.id(),
|
||||
target: 0
|
||||
}))))
|
||||
})
|
||||
.chain(std::iter::once_with(move || {
|
||||
(id, SendShipPacket::RareMonsterList(builder::room::build_rare_monster_list(rare_monster_list)))
|
||||
}))
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn request_room_list(id: ClientId,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
let active_room_list = rooms.iter()
|
||||
pub async fn request_room_list(id: ClientId,
|
||||
client_location: &ClientLocation,
|
||||
rooms: &Rooms)
|
||||
-> Vec<(ClientId, SendShipPacket)> {
|
||||
let active_room_list = rooms.stream()
|
||||
.enumerate()
|
||||
.filter_map(|(i, r)| {
|
||||
.filter_map(|(i, r)| async move {
|
||||
r.as_ref().map(|room| {
|
||||
RoomList {
|
||||
menu_id: ROOM_MENU_ID,
|
||||
item_id: i as u32,
|
||||
difficulty: room.get_difficulty_for_room_list(),
|
||||
players: client_location.get_clients_in_room(RoomId(i)).unwrap().len() as u8,
|
||||
name: libpso::utf8_to_utf16_array!(room.name, 16),
|
||||
episode: room.get_episode_for_room_list(),
|
||||
flags: room.get_flags_for_room_list(),
|
||||
}
|
||||
})
|
||||
let difficulty = room.get_difficulty_for_room_list();
|
||||
let name = libpso::utf8_to_utf16_array!(room.name, 16);
|
||||
let episode = room.get_episode_for_room_list();
|
||||
let flags = room.get_flags_for_room_list();
|
||||
async move {
|
||||
RoomList {
|
||||
menu_id: ROOM_MENU_ID,
|
||||
item_id: i as u32,
|
||||
difficulty,
|
||||
players: client_location.get_clients_in_room(RoomId(i)).await.unwrap().len() as u8,
|
||||
name,
|
||||
episode,
|
||||
flags,
|
||||
}
|
||||
}})
|
||||
});
|
||||
let baseroom: RoomList = RoomList {
|
||||
menu_id: ROOM_MENU_ID,
|
||||
@ -209,21 +224,25 @@ pub fn request_room_list(id: ClientId,
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
Box::new(vec![(id, SendShipPacket::RoomListResponse(RoomListResponse {
|
||||
vec![(id, SendShipPacket::RoomListResponse(RoomListResponse {
|
||||
baseroom,
|
||||
rooms: active_room_list.collect()
|
||||
}))].into_iter())
|
||||
rooms: futures::future::join_all(active_room_list.collect::<Vec<_>>().await).await
|
||||
}))]
|
||||
}
|
||||
|
||||
pub fn cool_62(id: ClientId,
|
||||
cool_62: &Like62ButCooler,
|
||||
client_location: &ClientLocation)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
pub async fn cool_62(id: ClientId,
|
||||
cool_62: Like62ButCooler,
|
||||
client_location: &ClientLocation)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let target = cool_62.flag as u8;
|
||||
let cool_62 = cool_62.clone();
|
||||
Box::new(client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Like62ButCooler(cool_62.clone()))
|
||||
}))
|
||||
Ok(client_location
|
||||
.get_client_neighbors(id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Like62ButCooler(cool_62.clone()))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
@ -3,47 +3,70 @@ use crate::common::serverstate::ClientId;
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
||||
use crate::entity::gateway::EntityGateway;
|
||||
|
||||
pub async fn update_config<EG: EntityGateway>(id: ClientId,
|
||||
update_config: &UpdateConfig,
|
||||
clients: &mut Clients,
|
||||
mut entity_gateway: EG)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||
client.character.config.update(update_config);
|
||||
entity_gateway.save_character(&client.character).await.unwrap();
|
||||
Box::new(None.into_iter())
|
||||
pub async fn update_config<EG>(id: ClientId,
|
||||
update_config: UpdateConfig,
|
||||
clients: &Clients,
|
||||
entity_gateway: &mut EG)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
Box::pin(async move {
|
||||
client.character.config.update(&update_config);
|
||||
entity_gateway.save_character(&client.character).await
|
||||
})}).await??;
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
pub async fn save_options<EG: EntityGateway>(id: ClientId,
|
||||
save_options: &SaveOptions,
|
||||
clients: &mut Clients,
|
||||
mut entity_gateway: EG)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
// TODO: don't unwrap?
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||
client.character.option_flags = save_options.options;
|
||||
entity_gateway.save_character(&client.character).await.unwrap();
|
||||
Box::new(None.into_iter())
|
||||
pub async fn save_options<EG>(id: ClientId,
|
||||
save_options: SaveOptions,
|
||||
clients: &Clients,
|
||||
entity_gateway: &mut EG)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
Box::pin(async move {
|
||||
client.character.option_flags = save_options.options;
|
||||
entity_gateway.save_character(&client.character).await
|
||||
})}).await??;
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
pub async fn keyboard_config<EG: EntityGateway>(id: ClientId,
|
||||
keyboard_config: &KeyboardConfig,
|
||||
clients: &mut Clients,
|
||||
mut entity_gateway: EG)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||
client.character.keyboard_config.update(keyboard_config);
|
||||
entity_gateway.save_character(&client.character).await.unwrap();
|
||||
Box::new(None.into_iter())
|
||||
pub async fn keyboard_config<EG>(id: ClientId,
|
||||
keyboard_config: KeyboardConfig,
|
||||
clients: &Clients,
|
||||
entity_gateway: &mut EG)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
Box::pin(async move {
|
||||
client.character.keyboard_config.update(&keyboard_config);
|
||||
entity_gateway.save_character(&client.character).await
|
||||
})}).await??;
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
pub async fn gamepad_config<EG: EntityGateway>(id: ClientId,
|
||||
gamepad_config: &GamepadConfig,
|
||||
clients: &mut Clients,
|
||||
mut entity_gateway: EG)
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||
client.character.gamepad_config.update(gamepad_config);
|
||||
entity_gateway.save_character(&client.character).await.unwrap();
|
||||
Box::new(None.into_iter())
|
||||
pub async fn gamepad_config<EG>(id: ClientId,
|
||||
gamepad_config: GamepadConfig,
|
||||
clients: &Clients,
|
||||
entity_gateway: &mut EG)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
clients.with_mut(id, |client| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
Box::pin(async move {
|
||||
client.character.gamepad_config.update(&gamepad_config);
|
||||
entity_gateway.save_character(&client.character).await
|
||||
})}).await??;
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
@ -5,19 +5,17 @@ use crate::common::interserver::Ship;
|
||||
use crate::ship::ship::{SendShipPacket, ShipError};
|
||||
use crate::ship::packet::builder;
|
||||
|
||||
pub fn ship_list(id: ClientId, ship_list: &[Ship])
|
||||
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
Box::new(vec![(id, SendShipPacket::ShipList(builder::ship::ship_list(ship_list)))].into_iter())
|
||||
pub fn ship_list(id: ClientId, ship_list: &[Ship]) -> Vec<(ClientId, SendShipPacket)> {
|
||||
vec![(id, SendShipPacket::ShipList(builder::ship::ship_list(ship_list)))]
|
||||
}
|
||||
|
||||
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 block_list(id: ClientId, shipname: &str, num_blocks: usize) -> Vec<(ClientId, SendShipPacket)> {
|
||||
vec![(id, SendShipPacket::ShipBlockList(ShipBlockList::new(shipname, num_blocks)))]
|
||||
}
|
||||
|
||||
pub fn selected_ship(id: ClientId, menuselect: &MenuSelect, ship_list: &[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))?;
|
||||
pub fn selected_ship(id: ClientId, menuselect: MenuSelect, ship_list: &[Ship])
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
||||
let ship = ship_list.get(menuselect.item as usize).ok_or_else(|| 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()))
|
||||
Ok(vec![(id, SendShipPacket::RedirectClient(RedirectClient::new(ip, ship.port)))])
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ use crate::ship::packet::builder;
|
||||
use crate::ship::items::tasks::trade_items;
|
||||
use crate::ship::location::{AreaClient, RoomId};
|
||||
use crate::entity::item::Meseta;
|
||||
use crate::ship::trade::ClientTradeState;
|
||||
|
||||
pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01);
|
||||
pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF);
|
||||
@ -48,15 +49,50 @@ pub enum TradeError {
|
||||
}
|
||||
|
||||
|
||||
|
||||
async fn do_trade_action<F>(id: ClientId,
|
||||
pkt: TradeRequest,
|
||||
client_location: &ClientLocation,
|
||||
target: u32,
|
||||
this: &mut ClientTradeState,
|
||||
other: &mut ClientTradeState,
|
||||
action: F)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> Result<(), ShipError>,
|
||||
{
|
||||
Ok(match action(this, other) {
|
||||
Ok(_) => {
|
||||
client_location.get_all_clients_by_client(id).await?.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(pkt.clone()))))
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
Err(_) => {
|
||||
// TODO: some sort of error logging?
|
||||
client_location.get_all_clients_by_client(id).await?.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))
|
||||
.collect()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// TODO: remove target
|
||||
pub async fn trade_request(id: ClientId,
|
||||
trade_request: &TradeRequest,
|
||||
target: u32,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_state: &mut ItemState,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
||||
trade_request: TradeRequest,
|
||||
target: u32,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
{
|
||||
let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet
|
||||
match trade_request.trade {
|
||||
@ -66,7 +102,7 @@ pub async fn trade_request(id: ClientId,
|
||||
if trades.in_trade(&id) {
|
||||
return Err(TradeError::ClientAlreadyInTrade.into())
|
||||
}
|
||||
let trade_partner = client_location.get_client_neighbors(id)?
|
||||
let trade_partner = client_location.get_client_neighbors(id).await?
|
||||
.into_iter()
|
||||
.find(|ac| {
|
||||
ac.local_client.id() == target as u8 //trade_request.client
|
||||
@ -76,209 +112,168 @@ pub async fn trade_request(id: ClientId,
|
||||
return Err(TradeError::OtherAlreadyInTrade.into())
|
||||
}
|
||||
trades.new_trade(&id, &trade_partner.client);
|
||||
Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
Ok(client_location.get_all_clients_by_client(id).await?.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})
|
||||
.collect())
|
||||
},
|
||||
TradeRequestInitializeCommand::Respond => {
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
||||
if this.status == TradeStatus::ReceivedRequest && other.status == TradeStatus::SentRequest {
|
||||
this.status = TradeStatus::Trading;
|
||||
other.status = TradeStatus::Trading;
|
||||
|
||||
let trade_request = trade_request.clone();
|
||||
Some(Box::new(client_location.get_all_clients_by_client(id).ok()?.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|| -> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
trades.remove_trade(&id);
|
||||
Box::new(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::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
trades
|
||||
.with(&id, |mut this, mut other| {
|
||||
let trade_request = trade_request.clone();
|
||||
async move {
|
||||
do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| {
|
||||
if this.status == TradeStatus::ReceivedRequest && other.status == TradeStatus::SentRequest {
|
||||
this.status = TradeStatus::Trading;
|
||||
other.status = TradeStatus::Trading;
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
Err(TradeError::MismatchedStatus.into())
|
||||
}
|
||||
}).await
|
||||
}}).await?
|
||||
}
|
||||
}
|
||||
},
|
||||
TradeRequestCommand::AddItem(item_id, amount) => {
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
||||
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
||||
let inventory = item_state.get_character_inventory(&client.character)?;
|
||||
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
||||
this.meseta += amount as usize;
|
||||
}
|
||||
else {
|
||||
let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?;
|
||||
|
||||
match &item.item {
|
||||
InventoryItemDetail::Individual(_) => {
|
||||
this.items.push(TradeItem::Individual(ClientItemId(item_id)));
|
||||
},
|
||||
InventoryItemDetail::Stacked(stacked_item) => {
|
||||
if stacked_item.count() < amount as usize {
|
||||
return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into());
|
||||
trades
|
||||
.with(&id, |mut this, mut other| {
|
||||
let trade_request = trade_request.clone();
|
||||
async move {
|
||||
let inventory = clients.with(this.client(), |client| {
|
||||
let item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
item_state.get_character_inventory(&client.character).await
|
||||
})}).await??;
|
||||
do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| {
|
||||
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
||||
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
||||
this.meseta += amount as usize;
|
||||
}
|
||||
else {
|
||||
let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or_else(|| ItemStateError::InvalidItemId(ClientItemId(item_id)))?;
|
||||
|
||||
match &item.item {
|
||||
InventoryItemDetail::Individual(_) => {
|
||||
this.items.push(TradeItem::Individual(ClientItemId(item_id)));
|
||||
},
|
||||
InventoryItemDetail::Stacked(stacked_item) => {
|
||||
if stacked_item.count() < amount as usize {
|
||||
return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into());
|
||||
}
|
||||
this.items.push(TradeItem::Stacked(ClientItemId(item_id), amount as usize));
|
||||
},
|
||||
}
|
||||
this.items.push(TradeItem::Stacked(ClientItemId(item_id), amount as usize));
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let trade_request = trade_request.clone();
|
||||
Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
}
|
||||
else {
|
||||
Err(TradeError::MismatchedStatus.into())
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|_err| {
|
||||
trades.remove_trade(&id);
|
||||
Box::new(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::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
else {
|
||||
Err(TradeError::MismatchedStatus.into())
|
||||
}
|
||||
}).await
|
||||
}}).await?
|
||||
},
|
||||
TradeRequestCommand::RemoveItem(item_id, amount) => {
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
||||
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
||||
let client = clients.get(&this.client())?; //.ok_or(ShipError::ClientNotFound(id)).ok()?;
|
||||
let inventory = item_state.get_character_inventory(&client.character).ok()?;
|
||||
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
||||
this.meseta -= amount as usize;
|
||||
}
|
||||
else {
|
||||
let item = inventory.get_by_client_id(&ClientItemId(item_id))?;
|
||||
trades
|
||||
.with(&id, |mut this, mut other| {
|
||||
let trade_request = trade_request.clone();
|
||||
async move {
|
||||
let inventory = clients.with(this.client(), |client| {
|
||||
let item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
item_state.get_character_inventory(&client.character).await
|
||||
})}).await??;
|
||||
do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| {
|
||||
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
||||
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
||||
this.meseta -= amount as usize;
|
||||
}
|
||||
else {
|
||||
let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or_else(|| ItemStateError::InvalidItemId(ClientItemId(item_id)))?;
|
||||
|
||||
match &item.item {
|
||||
InventoryItemDetail::Individual(_) => {
|
||||
this.items.retain(|item| {
|
||||
item.item_id() != ClientItemId(item_id)
|
||||
})
|
||||
},
|
||||
InventoryItemDetail::Stacked(_stacked_item) => {
|
||||
let trade_item_index = this.items.iter()
|
||||
.position(|item| {
|
||||
item.item_id() == ClientItemId(item_id)
|
||||
})?;
|
||||
match &item.item {
|
||||
InventoryItemDetail::Individual(_) => {
|
||||
this.items.retain(|item| {
|
||||
item.item_id() != ClientItemId(item_id)
|
||||
})
|
||||
},
|
||||
InventoryItemDetail::Stacked(_stacked_item) => {
|
||||
let trade_item_index = this.items.iter()
|
||||
.position(|item| {
|
||||
item.item_id() == ClientItemId(item_id)
|
||||
})
|
||||
.ok_or(TradeError::InvalidItemId(ClientItemId(item_id)))?;
|
||||
|
||||
match this.items[trade_item_index].stacked()?.1.cmp(&(amount as usize)) {
|
||||
std::cmp::Ordering::Greater => {
|
||||
*this.items[trade_item_index].stacked_mut()?.1 -= amount as usize;
|
||||
match this.items[trade_item_index].stacked().ok_or_else(|| ItemStateError::InvalidItemId(ClientItemId(item_id)))?.1.cmp(&(amount as usize)) {
|
||||
std::cmp::Ordering::Greater => {
|
||||
*this.items[trade_item_index].stacked_mut().ok_or_else(|| ItemStateError::InvalidItemId(ClientItemId(item_id)))?.1 -= amount as usize;
|
||||
},
|
||||
std::cmp::Ordering::Equal => {
|
||||
this.items.remove(trade_item_index);
|
||||
},
|
||||
std::cmp::Ordering::Less => {
|
||||
return Err(TradeError::SketchyTrade.into())
|
||||
}
|
||||
}
|
||||
},
|
||||
std::cmp::Ordering::Equal => {
|
||||
this.items.remove(trade_item_index);
|
||||
},
|
||||
std::cmp::Ordering::Less => {
|
||||
return None
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
let trade_request = trade_request.clone();
|
||||
Some(Box::new(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(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
else {
|
||||
Err(TradeError::MismatchedStatus.into())
|
||||
}
|
||||
|
||||
}).await
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|| {
|
||||
trades.remove_trade(&id);
|
||||
Box::new(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::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
}).await?
|
||||
},
|
||||
TradeRequestCommand::Confirm => {
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
||||
if status_is(&this.status, &[TradeStatus::Trading]) && status_is(&other.status, &[TradeStatus::Trading, TradeStatus::Confirmed]) {
|
||||
this.status = TradeStatus::Confirmed;
|
||||
|
||||
let trade_request = trade_request.clone();
|
||||
Some(Box::new(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(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
trades
|
||||
.with(&id, |mut this, mut other| {
|
||||
let trade_request = trade_request.clone();
|
||||
async move {
|
||||
do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| {
|
||||
if status_is(&this.status, &[TradeStatus::Trading]) && status_is(&other.status, &[TradeStatus::Trading, TradeStatus::Confirmed]) {
|
||||
this.status = TradeStatus::Confirmed;
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
Err(TradeError::MismatchedStatus.into())
|
||||
}
|
||||
}).await
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|| {
|
||||
trades.remove_trade(&id);
|
||||
Box::new(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::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
}).await?
|
||||
},
|
||||
TradeRequestCommand::FinalConfirm => {
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
||||
if this.status == TradeStatus::Confirmed && (other.status == TradeStatus::Confirmed || other.status == TradeStatus::FinalConfirm) {
|
||||
this.status = TradeStatus::FinalConfirm;
|
||||
|
||||
let trade_request = trade_request.clone();
|
||||
Some(Box::new(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(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
trades
|
||||
.with(&id, |mut this, mut other| {
|
||||
let trade_request = trade_request.clone();
|
||||
async move {
|
||||
do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| {
|
||||
if this.status == TradeStatus::Confirmed && (other.status == TradeStatus::Confirmed || other.status == TradeStatus::FinalConfirm) {
|
||||
this.status = TradeStatus::FinalConfirm;
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
Err(TradeError::MismatchedStatus.into())
|
||||
}
|
||||
}).await
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|| {
|
||||
trades.remove_trade(&id);
|
||||
Box::new(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::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
}).await?
|
||||
},
|
||||
TradeRequestCommand::Cancel => {
|
||||
trades.remove_trade(&id);
|
||||
Ok(Box::new(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::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))))
|
||||
trades.remove_trade(&id).await;
|
||||
Ok(client_location.get_all_clients_by_client(id).await?.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -292,25 +287,34 @@ fn status_is_not<const N: usize>(status: &TradeStatus, statuses: &[TradeStatus;
|
||||
!status_is(status, statuses)
|
||||
}
|
||||
|
||||
pub async fn inner_items_to_trade(id: ClientId,
|
||||
items_to_trade: &ItemsToTrade,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_state: &mut ItemState,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
async fn inner_items_to_trade(id: ClientId,
|
||||
items_to_trade: ItemsToTrade,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
{
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
let pkts = trades
|
||||
.with(&id, |mut this, other| async move {
|
||||
if status_is_not(&this.status, &[TradeStatus::FinalConfirm]) || status_is_not(&other.status, &[TradeStatus::FinalConfirm, TradeStatus::ItemsChecked]) {
|
||||
return Err(TradeError::MismatchedStatus.into())
|
||||
return Err(ShipError::from(TradeError::MismatchedStatus))
|
||||
}
|
||||
let other_client = other.client();
|
||||
let (this_inventory, other_inventory) = clients.with(this.client(), |client| {
|
||||
let item_state = item_state.clone();
|
||||
let clients = clients.clone();
|
||||
Box::pin(async move {
|
||||
let this = item_state.get_character_inventory(&client.character).await?;
|
||||
let other_inventory = clients.with(other_client, |client| {
|
||||
let item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
item_state.get_character_inventory(&client.character).await
|
||||
})}).await??;
|
||||
Ok::<_, ShipError>((this, other_inventory))
|
||||
})}).await??;
|
||||
|
||||
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
||||
let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?;
|
||||
let inventory = item_state.get_character_inventory(&client.character)?;
|
||||
|
||||
if items_to_trade.count as usize != (this.items.len() + (if this.meseta != 0 { 1 } else { 0 })) {
|
||||
if items_to_trade.count as usize != (this.items.len() + usize::from(this.meseta != 0)) {
|
||||
return Err(TradeError::MismatchedTradeItems.into())
|
||||
}
|
||||
|
||||
@ -323,8 +327,8 @@ pub async fn inner_items_to_trade(id: ClientId,
|
||||
return Err(TradeError::InvalidItemId(ClientItemId(item.item_id)).into())
|
||||
}
|
||||
let amount = u32::from_le_bytes(item.item_data2);
|
||||
let character_meseta = item_state.get_character_inventory(&client.character).map_err(|_| TradeError::InvalidMeseta)?.meseta;
|
||||
let other_character_meseta = item_state.get_character_inventory(&other_client.character).map_err(|_| TradeError::InvalidMeseta)?.meseta;
|
||||
let character_meseta = this_inventory.meseta;
|
||||
let other_character_meseta = other_inventory.meseta;
|
||||
if amount > character_meseta.0 {
|
||||
return Err(TradeError::InvalidMeseta.into())
|
||||
}
|
||||
@ -337,8 +341,8 @@ pub async fn inner_items_to_trade(id: ClientId,
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
let real_item = inventory.get_by_client_id(&ClientItemId(item.item_id))
|
||||
.ok_or(ItemStateError::InvalidItemId(ClientItemId(item.item_id)))?;
|
||||
let real_item = this_inventory.get_by_client_id(&ClientItemId(item.item_id))
|
||||
.ok_or_else(|| ItemStateError::InvalidItemId(ClientItemId(item.item_id)))?;
|
||||
let real_trade_item = this.items
|
||||
.iter()
|
||||
.find(|i| i.item_id() == ClientItemId(item.item_id))
|
||||
@ -379,194 +383,222 @@ pub async fn inner_items_to_trade(id: ClientId,
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
||||
.collect::<Result<Vec<_>, ShipError>>()?;
|
||||
|
||||
this.status = TradeStatus::ItemsChecked;
|
||||
if this.status == TradeStatus::ItemsChecked && other.status == TradeStatus::ItemsChecked {
|
||||
Ok(Box::new(vec![
|
||||
Ok(vec![
|
||||
(this.client(), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})),
|
||||
(other.client(), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})),
|
||||
].into_iter()))
|
||||
])
|
||||
}
|
||||
else {
|
||||
Ok(Box::new(None.into_iter()))
|
||||
Ok(Vec::new())
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|err| {
|
||||
log::warn!("trade error: {:?}", err);
|
||||
let (_this, other) = trades.remove_trade(&id);
|
||||
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| other.as_ref().map(|other| client.client == other.client() ).unwrap_or_else(|| false))
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
}).await?;
|
||||
match pkts {
|
||||
Ok(pkts) => Ok(pkts),
|
||||
Err(err) => {
|
||||
log::warn!("trade error: {:?}", err);
|
||||
let (_this, other) = trades.remove_trade(&id).await;
|
||||
Ok(client_location.get_all_clients_by_client(id).await?.into_iter()
|
||||
.filter(move |client| other.as_ref().map(|other| client.client == other.client() ).unwrap_or_else(|| false))
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn items_to_trade(id: ClientId,
|
||||
items_to_trade_pkt: &ItemsToTrade,
|
||||
items_to_trade_pkt: ItemsToTrade,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
{
|
||||
let t = inner_items_to_trade(id, items_to_trade_pkt, client_location, clients, item_state, trades).await;
|
||||
match t {
|
||||
Ok(p) => Ok(p),
|
||||
Err(err) => {
|
||||
log::warn!("atrade error: {:?}", err);
|
||||
let (_this, other) = trades.remove_trade(&id);
|
||||
Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter()
|
||||
.filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false))
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))))
|
||||
let (_this, other) = trades.remove_trade(&id).await;
|
||||
Ok(client_location.get_all_clients_by_client(id).await?.into_iter()
|
||||
.filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false))
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn trade_confirmed<EG>(id: ClientId,
|
||||
mut entity_gateway: EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_state: &mut ItemState,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
async fn trade_confirmed_inner<EG>(id: ClientId,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
enum TradeReady<'a> {
|
||||
enum TradeReady/*<'a>*/ {
|
||||
OnePlayer,
|
||||
BothPlayers(RoomId,
|
||||
(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState),
|
||||
(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)),
|
||||
(AreaClient, crate::ship::trade::ClientTradeState),
|
||||
(AreaClient, crate::ship::trade::ClientTradeState)),
|
||||
//(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState),
|
||||
//(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)),
|
||||
}
|
||||
|
||||
let trade_instructions = trades
|
||||
.with(&id, |this, other| -> Result<_, anyhow::Error> {
|
||||
if status_is_not(&this.status, &[TradeStatus::ItemsChecked]) || status_is_not(&other.status, &[TradeStatus::ItemsChecked, TradeStatus::TradeComplete]) {
|
||||
return Err(TradeError::MismatchedStatus.into())
|
||||
let trade = trades
|
||||
.with(&id, |mut this, other| {
|
||||
async move {
|
||||
if status_is_not(&this.status, &[TradeStatus::ItemsChecked]) || status_is_not(&other.status, &[TradeStatus::ItemsChecked, TradeStatus::TradeComplete]) {
|
||||
return Err(ShipError::TradeError(TradeError::MismatchedStatus))
|
||||
}
|
||||
this.status = TradeStatus::TradeComplete;
|
||||
|
||||
if this.status == TradeStatus::TradeComplete && other.status == TradeStatus::TradeComplete {
|
||||
let this_local_client = client_location.get_local_client(this.client()).await?;
|
||||
let other_local_client = client_location.get_local_client(other.client()).await?;
|
||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
Ok(TradeReady::BothPlayers(room_id,
|
||||
(this_local_client, /*this_client, */this.clone()),
|
||||
(other_local_client, /*other_client, */other.clone())))
|
||||
}
|
||||
else {
|
||||
Ok(TradeReady::OnePlayer)
|
||||
}
|
||||
}
|
||||
this.status = TradeStatus::TradeComplete;
|
||||
}).await??;
|
||||
|
||||
if this.status == TradeStatus::TradeComplete && other.status == TradeStatus::TradeComplete {
|
||||
let this_client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
||||
let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?;
|
||||
let this_local_client = client_location.get_local_client(this.client())?;
|
||||
let other_local_client = client_location.get_local_client(other.client())?;
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
match trade {
|
||||
TradeReady::OnePlayer => {
|
||||
Ok(Vec::new())
|
||||
},
|
||||
TradeReady::BothPlayers(_room_id, (this_local_client, /*this_client,*/ this), (other_local_client, /*other_client,*/ other)) => {
|
||||
let remove_item_packets = this.items
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(move |item| {
|
||||
(this_local_client, item)
|
||||
})
|
||||
.chain(other.items
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(move |item| {
|
||||
(other_local_client, item)
|
||||
}))
|
||||
.map(|(client, item)| {
|
||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(client, item.item_id(), item.amount() as u32))
|
||||
});
|
||||
|
||||
Ok(TradeReady::BothPlayers(room_id,
|
||||
(this_local_client, this_client, this.clone()),
|
||||
(other_local_client, other_client, other.clone())))
|
||||
}
|
||||
else {
|
||||
Ok(TradeReady::OnePlayer)
|
||||
}
|
||||
});
|
||||
let this_items = this.items.clone();
|
||||
let other_items = other.items.clone();
|
||||
let (this_new_items, other_new_items) = clients.with_many(
|
||||
[this_local_client.client, other_local_client.client],
|
||||
|[this_client, other_client]| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
let mut item_state = item_state.clone();
|
||||
Box::pin(async move {
|
||||
trade_items(&mut item_state,
|
||||
&mut entity_gateway,
|
||||
(&this_local_client, &this_client.character, &this_items, Meseta(this.meseta as u32)),
|
||||
(&other_local_client, &other_client.character, &other_items, Meseta(other.meseta as u32))).await
|
||||
})}).await??;
|
||||
|
||||
let create_item_packets = this_new_items
|
||||
.into_iter()
|
||||
.map(move |item| {
|
||||
(this_local_client, item)
|
||||
})
|
||||
.chain(other_new_items
|
||||
.into_iter()
|
||||
.map(move |item| {
|
||||
(other_local_client, item)
|
||||
}))
|
||||
.map(|(client, item)| {
|
||||
match item.item {
|
||||
InventoryItemDetail::Individual(individual_item) => {
|
||||
GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap())
|
||||
},
|
||||
InventoryItemDetail::Stacked(stacked_item) => {
|
||||
GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap())
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: this match needs to handle errors better
|
||||
match trade_instructions {
|
||||
Ok(Ok(trade)) => {
|
||||
match trade {
|
||||
TradeReady::OnePlayer => {
|
||||
Ok(Box::new(None.into_iter()) as Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>)
|
||||
},
|
||||
TradeReady::BothPlayers(_room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => {
|
||||
let remove_item_packets = this.items
|
||||
let meseta_packets = vec![(this_local_client, other_local_client, this.meseta), (other_local_client, this_local_client, other.meseta)]
|
||||
.into_iter()
|
||||
.filter(|(_, _, meseta)| *meseta != 0)
|
||||
.flat_map(|(this, other, meseta)| {
|
||||
[
|
||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(this, MESETA_ITEM_ID, meseta as u32)),
|
||||
GameMessage::CreateItem(builder::message::create_meseta(other, meseta)),
|
||||
]
|
||||
});
|
||||
|
||||
let clients_in_room = client_location.get_all_clients_by_client(id).await?;
|
||||
let traded_item_packets = remove_item_packets
|
||||
.chain(create_item_packets)
|
||||
.chain(meseta_packets)
|
||||
.flat_map(move |packet| {
|
||||
clients_in_room
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(move |item| {
|
||||
(this_local_client, item)
|
||||
})
|
||||
.chain(other.items
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(move |item| {
|
||||
(other_local_client, item)
|
||||
}))
|
||||
.map(|(client, item)| {
|
||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(client, item.item_id(), item.amount() as u32))
|
||||
});
|
||||
|
||||
let (this_new_items, other_new_items) = trade_items(item_state,
|
||||
&mut entity_gateway,
|
||||
(&this_local_client, &this_client.character, &this.items, Meseta(this.meseta as u32)),
|
||||
(&other_local_client, &other_client.character, &other.items, Meseta(other.meseta as u32))).await?;
|
||||
|
||||
let create_item_packets = this_new_items
|
||||
.into_iter()
|
||||
.map(move |item| {
|
||||
(this_local_client, item)
|
||||
})
|
||||
.chain(other_new_items
|
||||
.into_iter()
|
||||
.map(move |item| {
|
||||
(other_local_client, item)
|
||||
}))
|
||||
.map(|(client, item)| {
|
||||
match item.item {
|
||||
InventoryItemDetail::Individual(individual_item) => {
|
||||
GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap())
|
||||
},
|
||||
InventoryItemDetail::Stacked(stacked_item) => {
|
||||
GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap())
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let meseta_packets = vec![(this_local_client, other_local_client, this.meseta), (other_local_client, this_local_client, other.meseta)]
|
||||
.into_iter()
|
||||
.filter(|(_, _, meseta)| *meseta != 0)
|
||||
.flat_map(|(this, other, meseta)| {
|
||||
[
|
||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(this, MESETA_ITEM_ID, meseta as u32)),
|
||||
GameMessage::CreateItem(builder::message::create_meseta(other, meseta)),
|
||||
]
|
||||
});
|
||||
|
||||
let clients_in_room = client_location.get_all_clients_by_client(id)?;
|
||||
let traded_item_packets = remove_item_packets
|
||||
.chain(create_item_packets)
|
||||
.chain(meseta_packets)
|
||||
.flat_map(move |packet| {
|
||||
clients_in_room
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter_map(move |client| {
|
||||
match packet {
|
||||
GameMessage::PlayerNoLongerHasItem(ref no_longer) => {
|
||||
if client.local_client == no_longer.client {
|
||||
None
|
||||
}
|
||||
else {
|
||||
Some((client.client, SendShipPacket::Message(Message::new(packet.clone()))))
|
||||
}
|
||||
}
|
||||
_ => Some((client.client, SendShipPacket::Message(Message::new(packet.clone()))))
|
||||
.filter_map(move |client| {
|
||||
match packet {
|
||||
GameMessage::PlayerNoLongerHasItem(ref no_longer) => {
|
||||
if client.local_client == no_longer.client {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
else {
|
||||
Some((client.client, SendShipPacket::Message(Message::new(packet.clone()))))
|
||||
}
|
||||
}
|
||||
_ => Some((client.client, SendShipPacket::Message(Message::new(packet.clone()))))
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let close_trade = vec![
|
||||
(this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())),
|
||||
(other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default()))
|
||||
].into_iter();
|
||||
Ok(Box::new(traded_item_packets.chain(close_trade)))
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
let (_this, other) = trades.remove_trade(&id);
|
||||
Ok(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false))
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))))
|
||||
let close_trade = vec![
|
||||
(this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())),
|
||||
(other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default()))
|
||||
].into_iter();
|
||||
Ok(traded_item_packets.chain(close_trade).collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub async fn trade_confirmed<EG>(id: ClientId,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &Clients,
|
||||
item_state: &mut ItemState,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
||||
where
|
||||
EG: EntityGateway + Clone + 'static,
|
||||
{
|
||||
match trade_confirmed_inner(id, entity_gateway, client_location, clients, item_state, trades).await {
|
||||
Ok(result) => Ok(result),
|
||||
Err(_err) => {
|
||||
let (_this, other) = trades.remove_trade(&id).await;
|
||||
Ok(client_location.get_all_clients_by_client(id).await?.into_iter()
|
||||
.filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false))
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use std::fs::File;
|
||||
use std::io::{Read, Write, Cursor, Seek, SeekFrom};
|
||||
use std::path::PathBuf;
|
||||
use std::convert::TryInto;
|
||||
use async_std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use ages_prs::{LegacyPrsDecoder, LegacyPrsEncoder};
|
||||
@ -76,7 +77,7 @@ fn read_dat_section_header<T: Read + Seek>(cursor: &mut T, episode: &Episode, ma
|
||||
cursor.read_exact(&mut obj_data)?;
|
||||
let mut obj_cursor = Cursor::new(obj_data);
|
||||
|
||||
let objects = objects_from_stream(&mut obj_cursor, episode, map_area);
|
||||
let objects = objects_from_stream(&mut obj_cursor, episode, &map_area);
|
||||
Ok(DatBlock::Object(objects))
|
||||
},
|
||||
DAT_ENEMY_HEADER_ID => {
|
||||
@ -84,7 +85,7 @@ fn read_dat_section_header<T: Read + Seek>(cursor: &mut T, episode: &Episode, ma
|
||||
cursor.read_exact(&mut enemy_data)?;
|
||||
let mut enemy_cursor = Cursor::new(enemy_data);
|
||||
|
||||
let enemies = enemy_data_from_stream(&mut enemy_cursor, map_area, episode);
|
||||
let enemies = enemy_data_from_stream(&mut enemy_cursor, &map_area, episode);
|
||||
|
||||
Ok(DatBlock::Enemy(enemies))
|
||||
},
|
||||
@ -159,18 +160,18 @@ pub enum QuestLoadError {
|
||||
CouldNotLoadConfigFile,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Quest {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub full_description: String,
|
||||
pub language: u16,
|
||||
pub id: u16,
|
||||
pub bin_blob: Vec<u8>,
|
||||
pub dat_blob: Vec<u8>,
|
||||
pub enemies: Vec<Option<MapEnemy>>,
|
||||
pub objects: Vec<Option<MapObject>>,
|
||||
pub map_areas: MapAreaLookup,
|
||||
pub bin_blob: Arc<Vec<u8>>,
|
||||
pub dat_blob: Arc<Vec<u8>>,
|
||||
pub enemies: Vec<Option<MapEnemy>>, // TODO: Arc?
|
||||
pub objects: Vec<Option<MapObject>>, // TODO: Arc?
|
||||
pub map_areas: MapAreaLookup, // TODO: Arc?
|
||||
}
|
||||
|
||||
impl Quest {
|
||||
@ -196,8 +197,8 @@ impl Quest {
|
||||
full_description,
|
||||
id,
|
||||
language,
|
||||
bin_blob: prs_bin.into_inner().map_err(|_| QuestLoadError::CouldNotReadMetadata)?,
|
||||
dat_blob: prs_dat.into_inner().map_err(|_| QuestLoadError::CouldNotReadMetadata)?,
|
||||
bin_blob: Arc::new(prs_bin.into_inner().map_err(|_| QuestLoadError::CouldNotReadMetadata)?),
|
||||
dat_blob: Arc::new(prs_dat.into_inner().map_err(|_| QuestLoadError::CouldNotReadMetadata)?),
|
||||
enemies,
|
||||
objects,
|
||||
map_areas,
|
||||
@ -232,7 +233,7 @@ pub fn load_quest(bin_path: PathBuf, dat_path: PathBuf, quest_path: PathBuf) ->
|
||||
}
|
||||
|
||||
|
||||
pub fn load_quests(quest_path: &mut PathBuf) -> Result<QuestList, QuestLoadError> {
|
||||
pub fn load_quests(mut quest_path: PathBuf) -> Result<QuestList, QuestLoadError> {
|
||||
let mut f = File::open(quest_path.clone()).map_err(|_| QuestLoadError::CouldNotLoadConfigFile)?;
|
||||
let mut s = String::new();
|
||||
f.read_to_string(&mut s)?;
|
||||
@ -270,7 +271,7 @@ pub fn load_quests(quest_path: &mut PathBuf) -> Result<QuestList, QuestLoadError
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// the quest phantasmal world 4 uses the tower map twice, to do this it had to remap
|
||||
|
109
src/ship/room.rs
109
src/ship/room.rs
@ -1,9 +1,13 @@
|
||||
use std::collections::HashMap;
|
||||
use std::convert::{From, Into, TryFrom, TryInto};
|
||||
use std::path::PathBuf;
|
||||
use async_std::sync::{Arc, RwLock, RwLockReadGuard};
|
||||
use futures::future::BoxFuture;
|
||||
use futures::stream::{FuturesOrdered, Stream};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
use crate::ship::map::Maps;
|
||||
use crate::ship::drops::DropTable;
|
||||
use crate::entity::character::SectionID;
|
||||
@ -11,9 +15,106 @@ use crate::ship::monster::{load_monster_stats_table, MonsterType, MonsterStats};
|
||||
use crate::ship::map::area::MapAreaLookup;
|
||||
use crate::ship::map::enemy::RareMonsterAppearTable;
|
||||
use crate::ship::quests;
|
||||
use std::path::PathBuf;
|
||||
use crate::ship::ship::ShipError;
|
||||
use crate::ship::location::{MAX_ROOMS, RoomId};
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Rooms([Arc<RwLock<Option<RoomState>>>; MAX_ROOMS]);
|
||||
|
||||
impl Default for Rooms {
|
||||
fn default() -> Rooms {
|
||||
Rooms(core::array::from_fn(|_| Arc::new(RwLock::new(None))))
|
||||
}
|
||||
}
|
||||
|
||||
impl Rooms {
|
||||
pub async fn add(&self, room_id: RoomId, room: RoomState) -> Result<(), ShipError> {
|
||||
*self.0
|
||||
.get(room_id.0)
|
||||
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.write()
|
||||
.await = Some(room);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove(&self, room_id: RoomId) {
|
||||
if let Some(room) = self.0.get(room_id.0) {
|
||||
*room
|
||||
.write()
|
||||
.await = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn exists(&self, room_id: RoomId) -> bool {
|
||||
match self.0.get(room_id.0) {
|
||||
Some(room) => {
|
||||
room
|
||||
.read()
|
||||
.await
|
||||
.is_some()
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn with<'a, T, F>(&'a self, room_id: RoomId, func: F) -> Result<T, ShipError>
|
||||
where
|
||||
T: Send,
|
||||
F: for<'b> FnOnce(&'b RoomState) -> BoxFuture<'b, T> + Send + 'a
|
||||
{
|
||||
let room = self.0
|
||||
.get(room_id.0)
|
||||
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.read()
|
||||
.await;
|
||||
if let Some(room) = room.as_ref() {
|
||||
Ok(func(room).await)
|
||||
}
|
||||
else {
|
||||
Err(ShipError::InvalidRoom(room_id.0 as u32))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn with_mut<'a, T, F>(&'a self, room_id: RoomId, func: F) -> Result<T, ShipError>
|
||||
where
|
||||
T: Send,
|
||||
F: for<'b> FnOnce(&'b mut RoomState) -> BoxFuture<'b, T> + Send + 'a
|
||||
{
|
||||
let mut room = self.0
|
||||
.get(room_id.0)
|
||||
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.write()
|
||||
.await;
|
||||
|
||||
if let Some(room) = room.as_mut() {
|
||||
Ok(func(room).await)
|
||||
}
|
||||
else {
|
||||
Err(ShipError::InvalidRoom(room_id.0 as u32))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(&self, room_id: RoomId) -> RwLockReadGuard<Option<RoomState>> {
|
||||
self.0
|
||||
.get(room_id.0)
|
||||
.unwrap()
|
||||
.read()
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn stream(&self) -> impl Stream<Item = RwLockReadGuard<Option<RoomState>>> {
|
||||
self.0
|
||||
.iter()
|
||||
.map(|room| async move {
|
||||
room
|
||||
.read()
|
||||
.await
|
||||
})
|
||||
.collect::<FuturesOrdered<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("")]
|
||||
pub enum RoomCreationError {
|
||||
@ -279,7 +380,7 @@ impl RoomState {
|
||||
qpath.push(room_mode.to_string());
|
||||
qpath.push("quests.toml");
|
||||
let mut room_quests = Vec::new();
|
||||
let quest_list = match quests::load_quests(&mut qpath) {
|
||||
let quest_list = match quests::load_quests(qpath) {
|
||||
Ok(qlist) => qlist,
|
||||
Err(_) => return Err(RoomCreationError::CouldNotLoadQuests),
|
||||
};
|
||||
@ -292,7 +393,7 @@ impl RoomState {
|
||||
qpath.push(room_mode.episode().to_string());
|
||||
qpath.push("government/quests.toml");
|
||||
|
||||
let quest_list = match quests::load_quests(&mut qpath) {
|
||||
let quest_list = match quests::load_quests(qpath) {
|
||||
Ok(qlist) => qlist,
|
||||
Err(_) => return Err(RoomCreationError::CouldNotLoadQuests),
|
||||
};
|
||||
|
467
src/ship/ship.rs
467
src/ship/ship.rs
@ -2,17 +2,21 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use async_std::channel;
|
||||
use async_std::sync::{Arc, Mutex};
|
||||
|
||||
use rand::Rng;
|
||||
use thiserror::Error;
|
||||
|
||||
use libpso::packet::ship::*;
|
||||
use libpso::packet::login::{RedirectClient, Login, LoginResponse, Session, ShipList};
|
||||
use libpso::packet::login::{RedirectClient, Login, LoginResponse, ShipList};
|
||||
use libpso::packet::messages::*;
|
||||
use libpso::{PacketParseError, PSOPacket};
|
||||
use libpso::crypto::bb::PSOBBCipher;
|
||||
|
||||
use libpso::packet::ship::{BLOCK_MENU_ID, ROOM_MENU_ID};
|
||||
|
||||
|
||||
use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
|
||||
use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
|
||||
use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage};
|
||||
@ -20,24 +24,24 @@ use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, Lo
|
||||
use crate::login::character::SHIP_MENU_ID;
|
||||
|
||||
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||
use crate::entity::account::{UserAccountEntity, UserSettingsEntity};
|
||||
use crate::entity::character::{CharacterEntity, SectionID};
|
||||
use crate::entity::item;
|
||||
use crate::entity::character::SectionID;
|
||||
|
||||
use crate::ship::location::{ClientLocation, RoomLobby, MAX_ROOMS, ClientLocationError, GetNeighborError, GetClientsError, GetAreaError};
|
||||
use crate::ship::location::{ClientLocation, RoomLobby, ClientLocationError, RoomId};
|
||||
|
||||
use crate::ship::items;
|
||||
use crate::ship::room;
|
||||
use crate::ship::map::{MapsError, MapAreaError, MapArea};
|
||||
use crate::ship::map::{MapsError, MapAreaError};
|
||||
use crate::ship::packet::handler;
|
||||
use crate::ship::shops::{WeaponShop, ToolShop, ArmorShop, WeaponShopItem, ToolShopItem, ArmorShopItem};
|
||||
use crate::ship::shops::{WeaponShop, ToolShop, ArmorShop};
|
||||
use crate::ship::trade::TradeState;
|
||||
|
||||
// TODO: remove once stuff settles down
|
||||
pub use crate::ship::client::*;
|
||||
|
||||
pub const SHIP_PORT: u16 = 23423;
|
||||
pub const QUEST_CATEGORY_MENU_ID: u32 = 0xA2;
|
||||
pub const QUEST_SELECT_MENU_ID: u32 = 0xA3;
|
||||
pub type Rooms = [Option<room::RoomState>; MAX_ROOMS];
|
||||
pub type Clients = HashMap<ClientId, ClientState>;
|
||||
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ShipError {
|
||||
@ -50,13 +54,7 @@ pub enum ShipError {
|
||||
#[error("too many clients")]
|
||||
TooManyClients,
|
||||
#[error("client error location {0}")]
|
||||
ClientLocationError(#[from] ClientLocationError),
|
||||
#[error("get neighbor error {0}")]
|
||||
GetNeighborError(#[from] GetNeighborError),
|
||||
#[error("get clients error {0}")]
|
||||
GetClientsError(#[from] GetClientsError),
|
||||
#[error("get area error {0}")]
|
||||
GetAreaError(#[from] GetAreaError),
|
||||
ClientLocationError(ClientLocationError),
|
||||
#[error("maps error {0}")]
|
||||
MapsError(#[from] MapsError),
|
||||
#[error("map area error {0}")]
|
||||
@ -80,9 +78,9 @@ pub enum ShipError {
|
||||
#[error("box already dropped item {0} {1}")]
|
||||
BoxAlreadyDroppedItem(ClientId, u16),
|
||||
#[error("invalid quest category {0}")]
|
||||
InvalidQuestCategory(u32),
|
||||
InvalidQuestCategory(u16),
|
||||
#[error("invalid quest {0}")]
|
||||
InvalidQuest(u32),
|
||||
InvalidQuest(u16),
|
||||
#[error("invalid quest filename {0}")]
|
||||
InvalidQuestFilename(String),
|
||||
#[error("io error {0}")]
|
||||
@ -105,8 +103,21 @@ pub enum ShipError {
|
||||
TradeError(#[from] crate::ship::packet::handler::trade::TradeError),
|
||||
#[error("trade state error {0}")]
|
||||
TradeStateError(#[from] crate::ship::trade::TradeStateError),
|
||||
#[error("message error {0}")]
|
||||
MessageError(#[from] crate::ship::packet::handler::direct_message::MessageError),
|
||||
#[error("room creation error {0}")]
|
||||
RoomCreationError(#[from] room::RoomCreationError),
|
||||
#[error("channel send error {0}")]
|
||||
SendError(#[from] async_std::channel::SendError<ShipMessage>),
|
||||
}
|
||||
|
||||
impl<I: Into<ClientLocationError>> From<I> for ShipError {
|
||||
fn from(other: I) -> ShipError {
|
||||
ShipError::ClientLocationError(other.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RecvShipPacket {
|
||||
Login(Login),
|
||||
@ -190,7 +201,7 @@ impl RecvServerPacket for RecvShipPacket {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SendShipPacket {
|
||||
ShipWelcome(ShipWelcome),
|
||||
LoginResponse(LoginResponse),
|
||||
@ -275,79 +286,11 @@ impl SendServerPacket for SendShipPacket {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ItemDropLocation {
|
||||
pub map_area: MapArea,
|
||||
pub x: f32,
|
||||
pub z: f32,
|
||||
pub item_id: items::ClientItemId,
|
||||
}
|
||||
|
||||
pub struct LoadingQuest {
|
||||
pub header_bin: Option<QuestHeader>,
|
||||
pub header_dat: Option<QuestHeader>,
|
||||
//pub quest_chunk_bin: Option<Box<dyn Iterator<Item = >>>,
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub struct ClientState {
|
||||
pub user: UserAccountEntity,
|
||||
pub settings: UserSettingsEntity,
|
||||
pub character: CharacterEntity,
|
||||
session: Session,
|
||||
//guildcard: GuildCard,
|
||||
pub block: usize,
|
||||
pub item_drop_location: Option<ItemDropLocation>,
|
||||
pub done_loading_quest: bool,
|
||||
//pub loading_quest: Option<LoadingQuest>,
|
||||
pub area: Option<MapArea>,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
pub weapon_shop: Vec<WeaponShopItem>,
|
||||
pub tool_shop: Vec<ToolShopItem>,
|
||||
pub armor_shop: Vec<ArmorShopItem>,
|
||||
pub tek: Option<(items::ClientItemId, item::weapon::TekSpecialModifier, item::weapon::TekPercentModifier, i32)>,
|
||||
pub character_playtime: chrono::Duration,
|
||||
pub log_on_time: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
impl ClientState {
|
||||
pub fn new(user: UserAccountEntity, settings: UserSettingsEntity, character: CharacterEntity, session: Session) -> ClientState {
|
||||
let character_playtime = chrono::Duration::seconds(character.playtime as i64);
|
||||
ClientState {
|
||||
user,
|
||||
settings,
|
||||
character,
|
||||
session,
|
||||
block: 0,
|
||||
item_drop_location: None,
|
||||
done_loading_quest: false,
|
||||
area: None,
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
weapon_shop: Vec::new(),
|
||||
tool_shop: Vec::new(),
|
||||
armor_shop: Vec::new(),
|
||||
tek: None,
|
||||
character_playtime,
|
||||
log_on_time: chrono::Utc::now(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_playtime(&mut self) {
|
||||
let additional_playtime = chrono::Utc::now() - self.log_on_time;
|
||||
self.character.playtime = (self.character_playtime + additional_playtime).num_seconds() as u32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ItemShops {
|
||||
pub weapon_shop: HashMap<(room::Difficulty, SectionID), WeaponShop<rand_chacha::ChaCha20Rng>>,
|
||||
pub tool_shop: ToolShop<rand_chacha::ChaCha20Rng>,
|
||||
pub armor_shop: ArmorShop<rand_chacha::ChaCha20Rng>,
|
||||
pub weapon_shop: HashMap<(room::Difficulty, SectionID), Arc<Mutex<WeaponShop<rand_chacha::ChaCha20Rng>>>>,
|
||||
pub tool_shop: Arc<Mutex<ToolShop<rand_chacha::ChaCha20Rng>>>,
|
||||
pub armor_shop: Arc<Mutex<ArmorShop<rand_chacha::ChaCha20Rng>>>,
|
||||
}
|
||||
|
||||
impl Default for ItemShops {
|
||||
@ -359,20 +302,20 @@ impl Default for ItemShops {
|
||||
let mut weapon_shop = HashMap::new();
|
||||
for d in difficulty.iter() {
|
||||
for id in section_id.iter() {
|
||||
weapon_shop.insert((*d, *id), WeaponShop::new(*d, *id));
|
||||
weapon_shop.insert((*d, *id), Arc::new(Mutex::new(WeaponShop::new(*d, *id))));
|
||||
}
|
||||
}
|
||||
|
||||
ItemShops {
|
||||
weapon_shop,
|
||||
tool_shop: ToolShop::default(),
|
||||
armor_shop: ArmorShop::default(),
|
||||
tool_shop: Arc::new(Mutex::new(ToolShop::default())),
|
||||
armor_shop: Arc::new(Mutex::new(ArmorShop::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct ShipServerStateBuilder<EG: EntityGateway + Clone> {
|
||||
pub struct ShipServerStateBuilder<EG: EntityGateway + Clone + 'static> {
|
||||
entity_gateway: Option<EG>,
|
||||
name: Option<String>,
|
||||
ip: Option<Ipv4Addr>,
|
||||
@ -381,7 +324,7 @@ pub struct ShipServerStateBuilder<EG: EntityGateway + Clone> {
|
||||
num_blocks: usize,
|
||||
}
|
||||
|
||||
impl<EG: EntityGateway + Clone> Default for ShipServerStateBuilder<EG> {
|
||||
impl<EG: EntityGateway + Clone + 'static> Default for ShipServerStateBuilder<EG> {
|
||||
fn default() -> ShipServerStateBuilder<EG> {
|
||||
ShipServerStateBuilder {
|
||||
entity_gateway: None,
|
||||
@ -394,7 +337,7 @@ impl<EG: EntityGateway + Clone> Default for ShipServerStateBuilder<EG> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<EG: EntityGateway + Clone> ShipServerStateBuilder<EG> {
|
||||
impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> {
|
||||
#[must_use]
|
||||
pub fn gateway(mut self, entity_gateway: EG) -> ShipServerStateBuilder<EG> {
|
||||
self.entity_gateway = Some(entity_gateway);
|
||||
@ -435,12 +378,12 @@ impl<EG: EntityGateway + Clone> ShipServerStateBuilder<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 {
|
||||
entity_gateway: self.entity_gateway.unwrap(),
|
||||
clients: HashMap::new(),
|
||||
clients: Clients::default(),
|
||||
name: self.name.unwrap_or_else(|| "NAMENOTSET".into()),
|
||||
item_state: items::state::ItemState::default(),
|
||||
ip: self.ip.unwrap_or_else(|| Ipv4Addr::new(127,0,0,1)),
|
||||
port: self.port.unwrap_or(SHIP_PORT),
|
||||
shops: Box::new(ItemShops::default()),
|
||||
shops: ItemShops::default(),
|
||||
blocks: Blocks(blocks),
|
||||
|
||||
auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())),
|
||||
@ -451,37 +394,35 @@ impl<EG: EntityGateway + Clone> ShipServerStateBuilder<EG> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Block {
|
||||
client_location: Box<ClientLocation>,
|
||||
pub rooms: Box<Rooms>,
|
||||
}
|
||||
|
||||
impl Default for Block {
|
||||
fn default() -> Block {
|
||||
const SNONE: Option<room::RoomState> = None;
|
||||
const NONE: Rooms = [SNONE; MAX_ROOMS];
|
||||
Block {
|
||||
client_location: Box::new(ClientLocation::default()),
|
||||
rooms: Box::new(NONE),
|
||||
}
|
||||
}
|
||||
client_location: ClientLocation,
|
||||
pub rooms: room::Rooms,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
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))
|
||||
async fn get_from_client(&mut self, id: ClientId, clients: &Clients) -> Result<&mut Block, ShipError> {
|
||||
let block = clients.with(id, |client| Box::pin(async move {
|
||||
client.block
|
||||
})).await?;
|
||||
self.0
|
||||
.get_mut(block)
|
||||
.ok_or_else(|| ShipError::InvalidBlock(block))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub struct ShipServerState<EG: EntityGateway + Clone> {
|
||||
#[derive(Clone)]
|
||||
pub struct ShipServerState<EG: EntityGateway + Clone + 'static> {
|
||||
entity_gateway: EG,
|
||||
pub clients: Clients,
|
||||
name: String,
|
||||
item_state: items::state::ItemState,
|
||||
shops: Box<ItemShops>,
|
||||
shops: ItemShops,
|
||||
pub blocks: Blocks,
|
||||
|
||||
ip: Ipv4Addr,
|
||||
@ -489,127 +430,125 @@ pub struct ShipServerState<EG: EntityGateway + Clone> {
|
||||
|
||||
auth_token: AuthToken,
|
||||
ship_list: Vec<Ship>,
|
||||
shipgate_sender: Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
|
||||
shipgate_sender: Option<channel::Sender<ShipMessage>>,
|
||||
trades: TradeState,
|
||||
}
|
||||
|
||||
impl<EG: EntityGateway + Clone> ShipServerState<EG> {
|
||||
impl<EG: EntityGateway + Clone + 'static> ShipServerState<EG> {
|
||||
pub fn builder() -> ShipServerStateBuilder<EG> {
|
||||
ShipServerStateBuilder::default()
|
||||
}
|
||||
|
||||
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> {
|
||||
Ok(match &msg.msg {
|
||||
async fn message(&mut self, id: ClientId, msg: Message) -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
Ok(match msg.msg {
|
||||
GameMessage::RequestExp(request_exp) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::message::request_exp(id, request_exp, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut block.rooms).await?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::message::request_exp(id, request_exp, &mut self.entity_gateway, &block.client_location, &self.clients, &block.rooms).await?
|
||||
},
|
||||
GameMessage::PlayerDropItem(player_drop_item) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::message::player_drop_item(id, player_drop_item, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_state).await?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &block.client_location, &self.clients, &block.rooms, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::DropCoordinates(drop_coordinates) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::message::drop_coordinates(id, drop_coordinates, &block.client_location, &mut self.clients, &block.rooms)?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::message::drop_coordinates(id, drop_coordinates, &block.client_location, &self.clients, &block.rooms).await?
|
||||
},
|
||||
GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::message::no_longer_has_item(id, no_longer_has_item, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::message::no_longer_has_item(id, no_longer_has_item, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).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(_) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::message::update_player_position(id, msg, &mut self.clients, &block.client_location, &block.rooms)?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::message::update_player_position(id, msg, &self.clients, &block.client_location, &block.rooms).await?
|
||||
},
|
||||
GameMessage::ChargeAttack(charge_attack) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::message::charge_attack(id, charge_attack, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::message::charge_attack(id, charge_attack, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::PlayerUseItem(player_use_item) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::message::player_uses_item(id, player_use_item, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::message::player_uses_item(id, player_use_item, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::message::player_used_medical_center(id, player_used_medical_center, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::PlayerFeedMag(player_feed_mag) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::message::player_feed_mag(id, player_feed_mag, self.entity_gateway.clone(), &block.client_location, &self.clients, &mut self.item_state).await?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::message::player_feed_mag(id, player_feed_mag, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::PlayerEquipItem(player_equip_item) => {
|
||||
handler::message::player_equips_item(id, player_equip_item, self.entity_gateway.clone(), &self.clients, &mut self.item_state).await?
|
||||
handler::message::player_equips_item(id, player_equip_item, &mut self.entity_gateway, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::PlayerUnequipItem(player_unequip_item) => {
|
||||
handler::message::player_unequips_item(id, player_unequip_item, self.entity_gateway.clone(), &self.clients, &mut self.item_state).await?
|
||||
handler::message::player_unequips_item(id, player_unequip_item, &mut self.entity_gateway, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::SortItems(sort_items) => {
|
||||
handler::message::player_sorts_items(id, sort_items, self.entity_gateway.clone(), &self.clients, &mut self.item_state).await?
|
||||
handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::PlayerSoldItem(player_sold_item) => {
|
||||
handler::message::player_sells_item(id, player_sold_item, self.entity_gateway.clone(), &mut self.clients, &mut self.item_state).await?
|
||||
handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
_ => {
|
||||
let cmsg = msg.clone();
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
Box::new(block.client_location.get_client_neighbors(id).unwrap().into_iter()
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
block.client_location.get_client_neighbors(id).await.unwrap().into_iter()
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::Message(cmsg.clone()))
|
||||
}))
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn direct_message(&mut self, id: ClientId, msg: &DirectMessage) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
async fn direct_message(&mut self, id: ClientId, msg: DirectMessage) -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
let target = msg.flag;
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
Ok(match &msg.msg {
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
Ok(match msg.msg {
|
||||
GameMessage::GuildcardSend(guildcard_send) => {
|
||||
handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients)
|
||||
handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients).await?
|
||||
},
|
||||
GameMessage::RequestItem(request_item) => {
|
||||
handler::direct_message::request_item(id, request_item, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_state).await?
|
||||
handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &block.client_location, &self.clients, &block.rooms, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::PickupItem(pickup_item) => {
|
||||
handler::direct_message::pickup_item(id, pickup_item, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||
handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::BoxDropRequest(box_drop_request) => {
|
||||
handler::direct_message::request_box_item(id, box_drop_request, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_state).await?
|
||||
handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &block.client_location, &self.clients, &block.rooms, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::BankRequest(_bank_request) => {
|
||||
handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::BankInteraction(bank_interaction) => {
|
||||
handler::direct_message::bank_interaction(id, bank_interaction, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::ShopRequest(shop_request) => {
|
||||
handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &mut self.shops).await?
|
||||
handler::direct_message::shop_request(id, shop_request, &block.client_location, &self.clients, &block.rooms, &self.shops).await?
|
||||
},
|
||||
GameMessage::BuyItem(buy_item) => {
|
||||
handler::direct_message::buy_item(id, buy_item, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||
handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::TekRequest(tek_request) => {
|
||||
handler::direct_message::request_tek_item(id, tek_request, self.entity_gateway.clone(), &mut self.clients, &mut self.item_state).await?
|
||||
handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::TekAccept(tek_accept) => {
|
||||
handler::direct_message::accept_tek_item(id, tek_accept, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state).await?
|
||||
handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await?
|
||||
},
|
||||
GameMessage::TradeRequest(trade_request) => {
|
||||
handler::trade::trade_request(id, trade_request, target, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await?
|
||||
handler::trade::trade_request(id, trade_request, target, &block.client_location, &self.clients, &mut self.item_state, &mut self.trades).await?
|
||||
},
|
||||
_ => {
|
||||
let cmsg = msg.clone();
|
||||
Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
block.client_location.get_all_clients_by_client(id).await.unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(cmsg.clone()))
|
||||
}))
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -619,9 +558,10 @@ impl<EG: EntityGateway + Clone> ShipServerState<EG> {
|
||||
impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
||||
type SendPacket = SendShipPacket;
|
||||
type RecvPacket = RecvShipPacket;
|
||||
type Cipher = PSOBBCipher;
|
||||
type PacketError = anyhow::Error;
|
||||
|
||||
async fn on_connect(&mut self, _id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket>>, anyhow::Error> {
|
||||
async fn on_connect(&mut self, _id: ClientId) -> Result<Vec<OnConnect<Self::SendPacket, Self::Cipher>>, anyhow::Error> {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let mut server_key = [0u8; 48];
|
||||
@ -630,74 +570,81 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
||||
rng.fill(&mut client_key[..]);
|
||||
|
||||
Ok(vec![OnConnect::Packet(SendShipPacket::ShipWelcome(ShipWelcome::new(server_key, client_key))),
|
||||
OnConnect::Cipher((Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key)),
|
||||
Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key))))
|
||||
OnConnect::Cipher(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key),
|
||||
PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key))
|
||||
])
|
||||
}
|
||||
|
||||
async fn handle(&mut self, id: ClientId, pkt: &RecvShipPacket)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
if let Some(client) = self.clients.get_mut(&id) {
|
||||
async fn handle(&mut self, id: ClientId, pkt: RecvShipPacket) -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
if let Ok((char_id, char_playtime)) = self.clients.with_mut(id, |client| Box::pin(async move {
|
||||
client.update_playtime();
|
||||
self.entity_gateway.set_character_playtime(&client.character.id, client.character.playtime).await?;
|
||||
(client.character.id, client.character.playtime)
|
||||
})).await {
|
||||
self.entity_gateway.set_character_playtime(&char_id, char_playtime).await?;
|
||||
}
|
||||
|
||||
Ok(match pkt {
|
||||
RecvShipPacket::Login(login) => {
|
||||
Box::new(handler::auth::validate_login(id, login, self.entity_gateway.clone(), &mut self.clients, &mut self.item_state, &self.shipgate_sender, &self.name, self.blocks.0.len())
|
||||
.await?.into_iter().map(move |pkt| (id, pkt)))
|
||||
handler::auth::validate_login(id, login, &mut self.entity_gateway, &mut self.clients, &mut self.item_state, &self.shipgate_sender, &self.name, self.blocks.0.len())
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(move |pkt| (id, pkt))
|
||||
.collect()
|
||||
},
|
||||
RecvShipPacket::QuestDetailRequest(questdetailrequest) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
match questdetailrequest.menu {
|
||||
QUEST_SELECT_MENU_ID => handler::quest::quest_detail(id, questdetailrequest, &block.client_location, &mut block.rooms)?,
|
||||
QUEST_SELECT_MENU_ID => handler::quest::quest_detail(id, questdetailrequest, &block.client_location, &block.rooms).await?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
RecvShipPacket::MenuSelect(menuselect) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
match menuselect.menu {
|
||||
SHIP_MENU_ID => {
|
||||
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten();
|
||||
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).await.into_iter().into_iter().flatten();
|
||||
let select_ship = handler::ship::selected_ship(id, menuselect, &self.ship_list)?;
|
||||
Box::new(leave_lobby.chain(select_ship))
|
||||
leave_lobby.chain(select_ship).collect()
|
||||
}
|
||||
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_state)?.into_iter();
|
||||
Box::new(leave_lobby.chain(select_block))
|
||||
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).await.into_iter().into_iter().flatten();
|
||||
let select_block = handler::lobby::block_selected(id, menuselect, &self.clients, &self.item_state).await?.into_iter();
|
||||
leave_lobby.chain(select_block).collect()
|
||||
}
|
||||
ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms)?,
|
||||
QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &mut block.rooms)?,
|
||||
ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms).await?,
|
||||
QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &block.rooms).await?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
RecvShipPacket::QuestMenuSelect(questmenuselect) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::quest::player_chose_quest(id, questmenuselect, &mut self.clients, &block.client_location, &mut block.rooms)?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::quest::player_chose_quest(id, questmenuselect, &self.clients, &block.client_location, &block.rooms).await?
|
||||
},
|
||||
RecvShipPacket::MenuDetail(menudetail) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
Box::new(handler::lobby::get_room_tab_info(id, menudetail, &mut block.client_location, &self.clients, &mut block.rooms)?.into_iter())
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::lobby::get_room_tab_info(id, menudetail, &mut block.client_location, &self.clients).await?
|
||||
},
|
||||
RecvShipPacket::RoomPasswordReq(room_password_req) => {
|
||||
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))?
|
||||
.password {
|
||||
let menuselect = MenuSelect {
|
||||
menu: room_password_req.menu,
|
||||
item: room_password_req.item,
|
||||
};
|
||||
handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &mut block.rooms)?
|
||||
}
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
|
||||
let room_password = block.rooms.with(RoomId(room_password_req.item as usize), |room| Box::pin(async move {
|
||||
room.password
|
||||
})).await?;
|
||||
|
||||
if room_password_req.password == room_password {
|
||||
let menuselect = MenuSelect {
|
||||
menu: room_password_req.menu,
|
||||
item: room_password_req.item,
|
||||
};
|
||||
handler::room::join_room(id, menuselect, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms).await?
|
||||
}
|
||||
else {
|
||||
Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))].into_iter())
|
||||
vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))]
|
||||
}
|
||||
},
|
||||
RecvShipPacket::CharData(chardata) => {
|
||||
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_state)?.into_iter())
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_state).await?
|
||||
},
|
||||
RecvShipPacket::Message(msg) => {
|
||||
self.message(id, msg).await?
|
||||
@ -706,72 +653,72 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
||||
self.direct_message(id, msg).await?
|
||||
},
|
||||
RecvShipPacket::PlayerChat(msg) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
Box::new(handler::communication::player_chat(id, msg, &block.client_location, &self.clients)?)
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::communication::player_chat(id, msg, &block.client_location, &self.clients).await?
|
||||
},
|
||||
RecvShipPacket::CreateRoom(create_room) => {
|
||||
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_state, &mut block.rooms)?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::room::create_room(id, create_room, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms).await?
|
||||
},
|
||||
RecvShipPacket::RoomNameRequest(_req) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::room::room_name_request(id, &block.client_location, &block.rooms)
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::room::room_name_request(id, &block.client_location, &block.rooms).await?
|
||||
},
|
||||
RecvShipPacket::UpdateConfig(pkt) => {
|
||||
handler::settings::update_config(id, pkt, &mut self.clients, self.entity_gateway.clone()).await
|
||||
handler::settings::update_config(id, pkt, &self.clients, &mut self.entity_gateway).await?
|
||||
},
|
||||
RecvShipPacket::ViewInfoboardRequest(_pkt) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::communication::request_infoboard(id, &block.client_location, &self.clients)
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::communication::request_infoboard(id, &block.client_location, &self.clients).await?
|
||||
},
|
||||
RecvShipPacket::WriteInfoboard(pkt) => {
|
||||
handler::communication::write_infoboard(id, pkt, &mut self.clients, self.entity_gateway.clone()).await
|
||||
handler::communication::write_infoboard(id, pkt, &self.clients, &mut self.entity_gateway).await?
|
||||
},
|
||||
RecvShipPacket::RoomListRequest(_req) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::room::request_room_list(id, &block.client_location, &block.rooms)
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::room::request_room_list(id, &block.client_location, &block.rooms).await
|
||||
},
|
||||
RecvShipPacket::Like62ButCooler(cool62) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::room::cool_62(id, cool62, &block.client_location)
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::room::cool_62(id, cool62, &block.client_location).await?
|
||||
},
|
||||
RecvShipPacket::ClientCharacterData(_) => {
|
||||
// TOOD: validate this in some way?
|
||||
Box::new(None.into_iter())
|
||||
Vec::new()
|
||||
},
|
||||
RecvShipPacket::DoneBursting(_) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::room::done_bursting(id, &block.client_location, &mut block.rooms)
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::room::done_bursting(id, &block.client_location, &block.rooms).await?
|
||||
},
|
||||
RecvShipPacket::DoneBursting2(_) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::room::done_bursting(id, &block.client_location, &mut block.rooms)
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::room::done_bursting(id, &block.client_location, &block.rooms).await?
|
||||
},
|
||||
RecvShipPacket::LobbySelect(pkt) => {
|
||||
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_state, &mut block.rooms, self.entity_gateway.clone()).await?.into_iter())
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, &mut self.entity_gateway).await?
|
||||
},
|
||||
RecvShipPacket::RequestQuestList(rql) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::quest::send_quest_category_list(id, rql, &block.client_location, &mut block.rooms)?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::quest::send_quest_category_list(id, rql, &block.client_location, &block.rooms).await?
|
||||
},
|
||||
RecvShipPacket::QuestFileRequest(quest_file_request) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::quest::quest_file_request(id, quest_file_request, &block.client_location, &mut block.rooms)?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::quest::quest_file_request(id, quest_file_request, &block.client_location, &mut block.rooms).await?
|
||||
},
|
||||
RecvShipPacket::QuestChunkAck(quest_chunk_ack) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::quest::quest_chunk_ack(id, quest_chunk_ack, &block.client_location, &mut block.rooms)?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::quest::quest_chunk_ack(id, quest_chunk_ack, &block.client_location, &block.rooms).await?
|
||||
},
|
||||
RecvShipPacket::DoneLoadingQuest(_) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::quest::done_loading_quest(id, &mut self.clients, &block.client_location)?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::quest::done_loading_quest(id, &self.clients, &block.client_location).await?
|
||||
},
|
||||
RecvShipPacket::FullCharacterData(_full_character_data) => {
|
||||
Box::new(None.into_iter())
|
||||
Vec::new()
|
||||
},
|
||||
RecvShipPacket::SaveOptions(save_options) => {
|
||||
handler::settings::save_options(id, save_options, &mut self.clients, self.entity_gateway.clone()).await
|
||||
handler::settings::save_options(id, save_options, &self.clients, &mut self.entity_gateway).await?
|
||||
},
|
||||
RecvShipPacket::RequestShipList(_) => {
|
||||
handler::ship::ship_list(id, &self.ship_list)
|
||||
@ -780,52 +727,60 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
||||
handler::ship::block_list(id, &self.name, self.blocks.0.len())
|
||||
},
|
||||
RecvShipPacket::ItemsToTrade(items_to_trade) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::trade::items_to_trade(id, items_to_trade, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::trade::items_to_trade(id, items_to_trade, &block.client_location, &self.clients, &mut self.item_state, &mut self.trades).await?
|
||||
},
|
||||
RecvShipPacket::TradeConfirmed(_) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::trade::trade_confirmed(id, self.entity_gateway.clone(), &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await?
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state, &mut self.trades).await?
|
||||
},
|
||||
RecvShipPacket::KeyboardConfig(keyboard_config) => {
|
||||
handler::settings::keyboard_config(id, keyboard_config, &mut self.clients, self.entity_gateway.clone()).await
|
||||
handler::settings::keyboard_config(id, keyboard_config, &self.clients, &mut self.entity_gateway).await?
|
||||
},
|
||||
RecvShipPacket::GamepadConfig(gamepad_config) => {
|
||||
handler::settings::gamepad_config(id, gamepad_config, &mut self.clients, self.entity_gateway.clone()).await
|
||||
handler::settings::gamepad_config(id, gamepad_config, &self.clients, &mut self.entity_gateway).await?
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn on_disconnect(&mut self, id: ClientId) -> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||
let client = self.clients.get(&id).ok_or(ShipError::ClientNotFound(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 client = self.clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||
let area_client = block.client_location.get_local_client(id).await?;
|
||||
let neighbors = block.client_location.get_client_neighbors(id).await?;
|
||||
|
||||
let pkt = match block.client_location.get_area(id)? {
|
||||
let pkt = match block.client_location.get_area(id).await? {
|
||||
RoomLobby::Room(room) => {
|
||||
if neighbors.is_empty() {
|
||||
block.rooms[room.0] = None;
|
||||
block.rooms.remove(room).await;
|
||||
}
|
||||
let leader = block.client_location.get_room_leader(room)?;
|
||||
let leader = block.client_location.get_room_leader(room).await?;
|
||||
SendShipPacket::LeaveRoom(LeaveRoom::new(area_client.local_client.id(), leader.local_client.id()))
|
||||
},
|
||||
RoomLobby::Lobby(lobby) => {
|
||||
let leader = block.client_location.get_lobby_leader(lobby)?;
|
||||
let leader = block.client_location.get_lobby_leader(lobby).await?;
|
||||
SendShipPacket::LeaveLobby(LeaveLobby::new(area_client.local_client.id(), leader.local_client.id()))
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
if let Some(shipgate_sender) = self.shipgate_sender.as_ref() {
|
||||
shipgate_sender(ShipMessage::RemoveUser(client.user.id));
|
||||
shipgate_sender.send(ShipMessage::RemoveUser(client.user.id)).await;
|
||||
}
|
||||
|
||||
block.client_location.remove_client_from_area(id);
|
||||
self.item_state.remove_character_from_room(&client.character);
|
||||
block.client_location.remove_client_from_area(id).await;
|
||||
self.clients.with(id, |client| Box::pin(async move {
|
||||
self.item_state.remove_character_from_room(&client.character).await
|
||||
})).await?;
|
||||
*/
|
||||
|
||||
if let Some(mut client) = self.clients.remove(&id) {
|
||||
if let Some(mut client) = self.clients.remove(&id).await {
|
||||
client.user.at_ship = false;
|
||||
self.entity_gateway.save_user(&client.user).await;
|
||||
if let Some(shipgate_sender) = self.shipgate_sender.as_ref() {
|
||||
shipgate_sender.send(ShipMessage::RemoveUser(client.user.id)).await;
|
||||
}
|
||||
self.item_state.remove_character_from_room(&client.character).await
|
||||
}
|
||||
|
||||
Ok(neighbors.into_iter().map(|n| {
|
||||
@ -844,7 +799,7 @@ impl<EG: EntityGateway + Clone> InterserverActor for ShipServerState<EG> {
|
||||
async fn on_connect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
|
||||
vec![
|
||||
(id, ShipMessage::Authenticate(self.auth_token.clone())),
|
||||
(id, ShipMessage::NewShip(Ship {
|
||||
(id, ShipMessage::NewShip(Ship {
|
||||
name: self.name.clone(),
|
||||
ip: self.ip,
|
||||
port: self.port,
|
||||
@ -854,7 +809,7 @@ impl<EG: EntityGateway + Clone> InterserverActor for ShipServerState<EG> {
|
||||
]
|
||||
}
|
||||
|
||||
async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
|
||||
async fn on_action(&mut self, _id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
|
||||
match msg {
|
||||
LoginMessage::SendMail{..} => {
|
||||
Ok(Vec::new())
|
||||
@ -864,11 +819,15 @@ impl<EG: EntityGateway + Clone> InterserverActor for ShipServerState<EG> {
|
||||
Ok(Vec::new())
|
||||
},
|
||||
LoginMessage::RequestUsers => {
|
||||
/*
|
||||
Ok(self.clients.iter()
|
||||
.map(|(_, client)| {
|
||||
(id, ShipMessage::AddUser(client.user.id))
|
||||
})
|
||||
.collect())
|
||||
*/
|
||||
// TODO
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -876,4 +835,8 @@ impl<EG: EntityGateway + Clone> InterserverActor for ShipServerState<EG> {
|
||||
async fn on_disconnect(&mut self, _id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
async fn set_sender(&mut self, _server_id: ServerId, sender: channel::Sender<Self::SendMessage>) {
|
||||
self.shipgate_sender = Some(sender);
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ impl ShopItem for ArmorShopItem {
|
||||
armor: frame.armor,
|
||||
dfp: 0,
|
||||
evp: 0,
|
||||
slots: frame.slots as u8,
|
||||
slots: frame.slots,
|
||||
})
|
||||
},
|
||||
ArmorShopItem::Barrier(barrier) => {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::ship::items;
|
||||
use async_std::sync::{Arc, Mutex, MutexGuard};
|
||||
use futures::future::{Future, OptionFuture};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TradeItem {
|
||||
@ -67,7 +67,7 @@ impl ClientTradeState {
|
||||
pub fn client(&self) -> ClientId {
|
||||
self.client
|
||||
}
|
||||
|
||||
|
||||
pub fn other_client(&self) -> ClientId {
|
||||
self.other_client
|
||||
}
|
||||
@ -81,9 +81,9 @@ pub enum TradeStateError {
|
||||
MismatchedTrade(ClientId, ClientId),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct TradeState {
|
||||
trades: HashMap<ClientId, RefCell<ClientTradeState>>,
|
||||
trades: HashMap<ClientId, Arc<Mutex<ClientTradeState>>>,
|
||||
}
|
||||
|
||||
impl TradeState {
|
||||
@ -95,7 +95,7 @@ impl TradeState {
|
||||
meseta: 0,
|
||||
status: TradeStatus::SentRequest,
|
||||
};
|
||||
self.trades.insert(*sender, RefCell::new(state));
|
||||
self.trades.insert(*sender, Arc::new(Mutex::new(state)));
|
||||
|
||||
let state = ClientTradeState {
|
||||
client: *receiver,
|
||||
@ -104,33 +104,60 @@ impl TradeState {
|
||||
meseta: 0,
|
||||
status: TradeStatus::ReceivedRequest,
|
||||
};
|
||||
self.trades.insert(*receiver, RefCell::new(state));
|
||||
self.trades.insert(*receiver, Arc::new(Mutex::new(state)));
|
||||
}
|
||||
|
||||
pub fn in_trade(&self, client: &ClientId) -> bool {
|
||||
self.trades.contains_key(client)
|
||||
}
|
||||
|
||||
pub fn with<T, F> (&self, client: &ClientId, func: F) -> Result<T, TradeStateError>
|
||||
/*
|
||||
pub async fn with<'a, T, F, Fut> (&'a self, client: &ClientId, func: F) -> Result<T, TradeStateError>
|
||||
where
|
||||
F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> T
|
||||
F: FnOnce(MutexGuard<'a, ClientTradeState>, MutexGuard<'a, ClientTradeState>) -> Fut + 'a,
|
||||
Fut: Future<Output=T>
|
||||
{
|
||||
let mut c1 = self.trades.get(client).ok_or(TradeStateError::ClientNotInTrade(*client))?.borrow_mut();
|
||||
let mut c2 = self.trades.get(&c1.other_client).ok_or(TradeStateError::ClientNotInTrade(c1.other_client))?.borrow_mut();
|
||||
let c1 = self.trades.get(client).ok_or(TradeStateError::ClientNotInTrade(*client))?.lock().await;
|
||||
let c2 = self.trades.get(&c1.other_client).ok_or(TradeStateError::ClientNotInTrade(c1.other_client))?.lock().await;
|
||||
|
||||
// sanity check
|
||||
if c1.client != c2.other_client {
|
||||
return Err(TradeStateError::MismatchedTrade(c1.client, c2.client));
|
||||
}
|
||||
Ok(func(c1, c2).await)
|
||||
}
|
||||
*/
|
||||
pub async fn with<'a, T, F, Fut> (&'a self, client: &ClientId, func: F) -> Result<T, TradeStateError>
|
||||
where
|
||||
T: Send,
|
||||
//F: for<'b> FnOnce(&'b mut ClientTradeState, &'b mut ClientTradeState) -> BoxFuture<'b, T> + Send + 'a
|
||||
//F: for<'b> FnOnce(&'b mut ClientTradeState, &'b mut ClientTradeState) -> Fut + Send + 'a,
|
||||
F: FnOnce(MutexGuard<'a, ClientTradeState>, MutexGuard<'a, ClientTradeState>) -> Fut + 'a,
|
||||
Fut: Future<Output = T>,
|
||||
{
|
||||
let c1 = self.trades
|
||||
.get(client)
|
||||
.ok_or(TradeStateError::ClientNotInTrade(*client))?
|
||||
.lock()
|
||||
.await;
|
||||
let c2 = self.trades
|
||||
.get(&c1.other_client)
|
||||
.ok_or(TradeStateError::ClientNotInTrade(c1.other_client))?
|
||||
.lock()
|
||||
.await;
|
||||
|
||||
Ok(func(&mut c1, &mut c2))
|
||||
if c1.client != c2.other_client {
|
||||
return Err(TradeStateError::MismatchedTrade(c1.client, c2.client));
|
||||
}
|
||||
|
||||
Ok(func(c1, c2).await)
|
||||
}
|
||||
|
||||
// TODO: is it possible for this to not return Options?
|
||||
pub fn remove_trade(&mut self, client: &ClientId) -> (Option<ClientTradeState>, Option<ClientTradeState>) {
|
||||
let c1 = self.trades.remove(client).map(|c| c.into_inner());
|
||||
pub async fn remove_trade(&mut self, client: &ClientId) -> (Option<ClientTradeState>, Option<ClientTradeState>) {
|
||||
let c1 = OptionFuture::from(self.trades.remove(client).map(|c| async move {c.lock().await.clone()} )).await;
|
||||
let c2 = if let Some(ref state) = c1 {
|
||||
self.trades.remove(&state.other_client).map(|c| c.into_inner())
|
||||
OptionFuture::from(self.trades.remove(&state.other_client).map(|c| async move {c.lock().await.clone()})).await
|
||||
}
|
||||
else {
|
||||
None
|
||||
|
@ -37,7 +37,7 @@ pub async fn new_user_character<EG: EntityGateway + Clone>(entity_gateway: &mut
|
||||
pub async fn log_in_char<EG: EntityGateway + Clone>(ship: &mut ShipServerState<EG>, id: ClientId, username: &str, password: &str) {
|
||||
let username = username.to_string();
|
||||
let password = password.to_string();
|
||||
ship.handle(id, &RecvShipPacket::Login(Login {
|
||||
ship.handle(id, RecvShipPacket::Login(Login {
|
||||
tag: 0,
|
||||
guildcard: 0,
|
||||
version: 0,
|
||||
@ -49,13 +49,13 @@ pub async fn log_in_char<EG: EntityGateway + Clone>(ship: &mut ShipServerState<E
|
||||
unknown3: [0; 40],
|
||||
hwinfo: [0; 8],
|
||||
session: Session::new(),
|
||||
})).await.unwrap().for_each(drop);
|
||||
})).await.unwrap();
|
||||
}
|
||||
|
||||
pub async fn join_lobby<EG: EntityGateway + Clone>(ship: &mut ShipServerState<EG>, id: ClientId) {
|
||||
ship.handle(id, &RecvShipPacket::CharData(CharData {
|
||||
ship.handle(id, RecvShipPacket::CharData(CharData {
|
||||
_unknown: [0; 0x828]
|
||||
})).await.unwrap().for_each(drop);
|
||||
})).await.unwrap();
|
||||
}
|
||||
|
||||
pub async fn create_room<EG: EntityGateway + Clone>(ship: &mut ShipServerState<EG>, id: ClientId, name: &str, password: &str) {
|
||||
@ -63,14 +63,14 @@ pub async fn create_room<EG: EntityGateway + Clone>(ship: &mut ShipServerState<E
|
||||
}
|
||||
|
||||
pub async fn leave_room<EG: EntityGateway + Clone>(ship: &mut ShipServerState<EG>, id: ClientId) {
|
||||
ship.handle(id, &RecvShipPacket::LobbySelect(LobbySelect {
|
||||
ship.handle(id, RecvShipPacket::LobbySelect(LobbySelect {
|
||||
menu: 3,
|
||||
lobby: 0,
|
||||
})).await.unwrap().for_each(drop);
|
||||
})).await.unwrap();
|
||||
}
|
||||
|
||||
pub async fn create_room_with_difficulty<EG: EntityGateway + Clone>(ship: &mut ShipServerState<EG>, id: ClientId, name: &str, password: &str, difficulty: Difficulty) {
|
||||
ship.handle(id, &RecvShipPacket::CreateRoom(CreateRoom {
|
||||
ship.handle(id, RecvShipPacket::CreateRoom(CreateRoom {
|
||||
unknown: [0; 2],
|
||||
name: utf8_to_utf16_array!(name, 16),
|
||||
password: utf8_to_utf16_array!(password, 16),
|
||||
@ -80,14 +80,14 @@ pub async fn create_room_with_difficulty<EG: EntityGateway + Clone>(ship: &mut S
|
||||
episode: 1,
|
||||
single_player: 0,
|
||||
padding: [0; 3],
|
||||
})).await.unwrap().for_each(drop);
|
||||
ship.handle(id, &RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap().for_each(drop);
|
||||
})).await.unwrap();
|
||||
ship.handle(id, RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap();
|
||||
}
|
||||
|
||||
pub async fn join_room<EG: EntityGateway + Clone>(ship: &mut ShipServerState<EG>, id: ClientId, room_id: u32) {
|
||||
ship.handle(id, &RecvShipPacket::MenuSelect(MenuSelect {
|
||||
ship.handle(id, RecvShipPacket::MenuSelect(MenuSelect {
|
||||
menu: ROOM_MENU_ID,
|
||||
item: room_id,
|
||||
})).await.unwrap().for_each(drop);
|
||||
ship.handle(id, &RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap().for_each(drop);
|
||||
})).await.unwrap();
|
||||
ship.handle(id, RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap();
|
||||
}
|
||||
|
@ -38,10 +38,10 @@ async fn test_bank_items_sent_in_character_login() {
|
||||
.build());
|
||||
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::MenuSelect(MenuSelect {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::MenuSelect(MenuSelect {
|
||||
menu: BLOCK_MENU_ID,
|
||||
item: 1,
|
||||
})).await.unwrap().collect::<Vec<_>>();
|
||||
})).await.unwrap();
|
||||
|
||||
assert!(matches!(&packets[0], (_, SendShipPacket::FullCharacter(fc)) if fc.character.bank.items[0].data1[0..3] == [0x00, 0x08, 0x04] ));
|
||||
}
|
||||
@ -78,11 +78,11 @@ async fn test_request_bank_items() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(matches!(&packets[0], (_, SendShipPacket::BankItemList (bank_item_list))
|
||||
if bank_item_list.item_count == 3
|
||||
@ -122,11 +122,11 @@ async fn test_request_stacked_bank_items() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(matches!(&packets[0], (_, SendShipPacket::BankItemList (bank_item_list))
|
||||
if bank_item_list.item_count == 1
|
||||
@ -187,11 +187,11 @@ async fn test_request_bank_items_sorted() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(matches!(&packets[0], (_, SendShipPacket::BankItemList (bank_item_list))
|
||||
if bank_item_list.item_count == 3
|
||||
@ -247,13 +247,13 @@ async fn test_deposit_individual_item() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10001,
|
||||
@ -261,7 +261,7 @@ async fn test_deposit_individual_item() {
|
||||
item_amount: 0,
|
||||
meseta_amount: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(packets.len() == 2);
|
||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)}))
|
||||
@ -313,13 +313,13 @@ async fn test_deposit_stacked_item() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
@ -327,7 +327,7 @@ async fn test_deposit_stacked_item() {
|
||||
item_amount: 3,
|
||||
meseta_amount: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(packets.len() == 2);
|
||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)}))
|
||||
@ -374,13 +374,13 @@ async fn test_deposit_partial_stacked_item() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
@ -388,7 +388,7 @@ async fn test_deposit_partial_stacked_item() {
|
||||
item_amount: 2,
|
||||
meseta_amount: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(packets.len() == 2);
|
||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)}))
|
||||
@ -455,13 +455,13 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
@ -469,7 +469,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
|
||||
item_amount: 2,
|
||||
meseta_amount: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(packets.len() == 2);
|
||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)}))
|
||||
@ -525,13 +525,13 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
@ -603,13 +603,13 @@ async fn test_deposit_individual_item_in_full_bank() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
@ -675,13 +675,13 @@ async fn test_deposit_stacked_item_in_full_bank() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
@ -761,13 +761,13 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
@ -775,7 +775,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
|
||||
item_amount: 2,
|
||||
meseta_amount: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||
assert_eq!(bank_items.items.len(), 200);
|
||||
@ -801,13 +801,13 @@ async fn test_deposit_meseta() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
@ -815,7 +815,7 @@ async fn test_deposit_meseta() {
|
||||
item_amount: 0,
|
||||
meseta_amount: 23,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||
@ -838,13 +838,13 @@ async fn test_deposit_too_much_meseta() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
@ -877,13 +877,13 @@ async fn test_deposit_meseta_when_bank_is_maxed() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
@ -935,13 +935,13 @@ async fn test_withdraw_individual_item() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x20000,
|
||||
@ -949,7 +949,7 @@ async fn test_withdraw_individual_item() {
|
||||
item_amount: 0,
|
||||
meseta_amount: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(packets.len() == 2);
|
||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
|
||||
@ -995,13 +995,13 @@ async fn test_withdraw_stacked_item() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x20000,
|
||||
@ -1009,7 +1009,7 @@ async fn test_withdraw_stacked_item() {
|
||||
item_amount: 3,
|
||||
meseta_amount: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(packets.len() == 2);
|
||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
|
||||
@ -1054,13 +1054,13 @@ async fn test_withdraw_partial_stacked_item() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x20000,
|
||||
@ -1068,7 +1068,7 @@ async fn test_withdraw_partial_stacked_item() {
|
||||
item_amount: 2,
|
||||
meseta_amount: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(packets.len() == 2);
|
||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
|
||||
@ -1132,13 +1132,13 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x20000,
|
||||
@ -1146,7 +1146,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
|
||||
item_amount: 2,
|
||||
meseta_amount: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(packets.len() == 2);
|
||||
assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
|
||||
@ -1204,13 +1204,13 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x20000,
|
||||
@ -1282,13 +1282,13 @@ async fn test_withdraw_individual_item_in_full_inventory() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x20000,
|
||||
@ -1350,13 +1350,13 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x20000,
|
||||
@ -1437,13 +1437,13 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x20000,
|
||||
@ -1451,7 +1451,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
|
||||
item_amount: 2,
|
||||
meseta_amount: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||
assert!(bank_items.items.len() == 0);
|
||||
@ -1480,13 +1480,13 @@ async fn test_withdraw_meseta() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
@ -1494,7 +1494,7 @@ async fn test_withdraw_meseta() {
|
||||
item_amount: 0,
|
||||
meseta_amount: 23,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap();
|
||||
@ -1517,13 +1517,13 @@ async fn test_withdraw_too_much_meseta() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packet = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packet = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
@ -1556,13 +1556,13 @@ async fn test_withdraw_meseta_inventory_is_maxed() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packet = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
let packet = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
|
@ -21,9 +21,9 @@ async fn test_save_options() {
|
||||
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::SaveOptions(SaveOptions{
|
||||
ship.handle(ClientId(1), RecvShipPacket::SaveOptions(SaveOptions{
|
||||
options: 12345,
|
||||
})).await.unwrap().for_each(drop);
|
||||
})).await.unwrap();
|
||||
|
||||
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let char = characters[0].as_ref().unwrap();
|
||||
@ -67,7 +67,7 @@ async fn test_change_keyboard_mappings() {
|
||||
|
||||
// update from default2 to default4
|
||||
// the client simply sends the full 364 bytes...
|
||||
ship.handle(ClientId(1), &RecvShipPacket::KeyboardConfig(KeyboardConfig{
|
||||
ship.handle(ClientId(1), RecvShipPacket::KeyboardConfig(KeyboardConfig{
|
||||
keyboard_config: [
|
||||
0, 0, 0, 0, 117, 0, 0, 0, 0, 0, 0, 0, 105, 0, 0, 0,
|
||||
0, 0, 0, 0, 113, 0, 0, 0, 0, 0, 0, 0, 115, 0, 0, 0,
|
||||
@ -93,7 +93,7 @@ async fn test_change_keyboard_mappings() {
|
||||
0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0,
|
||||
0, 0, 0, 0, 51, 0, 0, 0, 1, 0, 0, 0
|
||||
],
|
||||
})).await.unwrap().for_each(drop);
|
||||
})).await.unwrap();
|
||||
|
||||
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let char = characters[0].as_ref().unwrap();
|
||||
|
18
tests/test_character_data.rs
Normal file
18
tests/test_character_data.rs
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
use elseware::common::serverstate::{ClientId, ServerState};
|
||||
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
||||
use elseware::entity::item;
|
||||
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
|
||||
|
||||
use libpso::packet::ship::*;
|
||||
use libpso::packet::messages::*;
|
||||
|
||||
#[path = "common.rs"]
|
||||
mod common;
|
||||
use common::*;
|
||||
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_character_data_includes_material_usage() {
|
||||
}
|
||||
*/
|
@ -3,6 +3,7 @@ use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
||||
use elseware::common::leveltable::CharacterLevelTable;
|
||||
use elseware::ship::ship::{ShipServerState, SendShipPacket, RecvShipPacket};
|
||||
use elseware::ship::monster::MonsterType;
|
||||
use elseware::ship::location::RoomId;
|
||||
|
||||
use libpso::packet::ship::*;
|
||||
use libpso::packet::messages::*;
|
||||
@ -25,27 +26,30 @@ async fn test_character_gains_exp() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
let (enemy_id, exp) = {
|
||||
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
|
||||
let (enemy_id, map_enemy) = (0..).filter_map(|i| {
|
||||
room.maps.enemy_by_id(i).map(|enemy| {
|
||||
(i, enemy)
|
||||
}).ok()
|
||||
}).next().unwrap();
|
||||
let map_enemy_stats = room.monster_stats.get(&map_enemy.monster).unwrap();
|
||||
(enemy_id, map_enemy_stats.exp)
|
||||
//let room = ship.blocks.0[0].rooms.get(RoomId(0)).as_ref().unwrap();
|
||||
ship.blocks.0[0].rooms.with(RoomId(0), |room| Box::pin(async move {
|
||||
let (enemy_id, map_enemy) = (0..).filter_map(|i| {
|
||||
room.maps.enemy_by_id(i).map(|enemy| {
|
||||
(i, enemy)
|
||||
}).ok()
|
||||
}).next().unwrap();
|
||||
let map_enemy_stats = room.monster_stats.get(&map_enemy.monster).unwrap();
|
||||
(enemy_id, map_enemy_stats.exp)
|
||||
})).await.unwrap()
|
||||
};
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::RequestExp(RequestExp{
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::RequestExp(RequestExp {
|
||||
client: enemy_id as u8,
|
||||
target: 16,
|
||||
enemy_id: enemy_id as u16,
|
||||
client_id: 0,
|
||||
unused: 0,
|
||||
last_hitter: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1 = ship.clients.get(&ClientId(1)).unwrap();
|
||||
assert!(exp == c1.character.exp);
|
||||
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
||||
assert!(exp == client.character.exp);
|
||||
})).await.unwrap();
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
@ -63,29 +67,29 @@ async fn test_character_levels_up() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
let enemy_id = {
|
||||
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
|
||||
let enemy_id = ship.blocks.0[0].rooms.with(RoomId(0), |room| Box::pin(async move {
|
||||
(0..).filter_map(|i| {
|
||||
room.maps.enemy_by_id(i).map(|_| {
|
||||
i
|
||||
}).ok()
|
||||
}).next().unwrap()
|
||||
};
|
||||
})).await.unwrap();
|
||||
|
||||
let levelup_pkt = ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::RequestExp(RequestExp{
|
||||
let levelup_pkt = ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::RequestExp(RequestExp {
|
||||
client: enemy_id as u8,
|
||||
target: 16,
|
||||
enemy_id: enemy_id as u16,
|
||||
client_id: 0,
|
||||
unused: 0,
|
||||
last_hitter: 1,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(matches!(levelup_pkt[1].1, SendShipPacket::Message(Message {msg: GameMessage::PlayerLevelUp(PlayerLevelUp {lvl: 2, ..})})));
|
||||
|
||||
let leveltable = CharacterLevelTable::default();
|
||||
let c1 = ship.clients.get(&ClientId(1)).unwrap();
|
||||
assert!(leveltable.get_level_from_exp(c1.character.char_class, c1.character.exp) == 2);
|
||||
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
||||
assert!(leveltable.get_level_from_exp(client.character.char_class, client.character.exp) == 2)
|
||||
})).await.unwrap();
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
@ -102,34 +106,36 @@ async fn test_character_levels_up_multiple_times() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
let (enemy_id, exp) = {
|
||||
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
|
||||
let (enemy_id, map_enemy) = (0..).filter_map(|i| {
|
||||
room.maps.enemy_by_id(i).ok().and_then(|enemy| {
|
||||
if enemy.monster == MonsterType::DarkFalz2 {
|
||||
Some((i, enemy))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}).next().unwrap();
|
||||
let map_enemy_stats = room.monster_stats.get(&map_enemy.monster).unwrap();
|
||||
(enemy_id, map_enemy_stats.exp)
|
||||
ship.blocks.0[0].rooms.with(RoomId(0), |room| Box::pin(async move {
|
||||
let (enemy_id, map_enemy) = (0..).filter_map(|i| {
|
||||
room.maps.enemy_by_id(i).ok().and_then(|enemy| {
|
||||
if enemy.monster == MonsterType::DarkFalz2 {
|
||||
Some((i, enemy))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}).next().unwrap();
|
||||
let map_enemy_stats = room.monster_stats.get(&map_enemy.monster).unwrap();
|
||||
(enemy_id, map_enemy_stats.exp)
|
||||
})).await.unwrap()
|
||||
};
|
||||
|
||||
let levelup_pkt = ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::RequestExp(RequestExp{
|
||||
let levelup_pkt = ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::RequestExp(RequestExp {
|
||||
client: enemy_id as u8,
|
||||
target: 16,
|
||||
enemy_id: enemy_id as u16,
|
||||
client_id: 0,
|
||||
unused: 0,
|
||||
last_hitter: 1,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert!(matches!(levelup_pkt[1].1, SendShipPacket::Message(Message {msg: GameMessage::PlayerLevelUp(PlayerLevelUp {lvl: 8, ..})})));
|
||||
|
||||
let c1 = ship.clients.get(&ClientId(1)).unwrap();
|
||||
assert!(exp == c1.character.exp);
|
||||
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
||||
assert!(exp == client.character.exp);
|
||||
})).await.unwrap();
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
@ -151,8 +157,7 @@ async fn test_one_character_gets_full_exp_and_other_attacker_gets_partial() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
let (enemy_id, exp) = {
|
||||
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
|
||||
let (enemy_id, exp) = ship.blocks.0[0].rooms.with(RoomId(0), |room| Box::pin(async move {
|
||||
let (enemy_id, map_enemy) = (0..).filter_map(|i| {
|
||||
room.maps.enemy_by_id(i).ok().and_then(|enemy| {
|
||||
if enemy.monster == MonsterType::DarkFalz2 {
|
||||
@ -165,28 +170,30 @@ async fn test_one_character_gets_full_exp_and_other_attacker_gets_partial() {
|
||||
}).next().unwrap();
|
||||
let map_enemy_stats = room.monster_stats.get(&map_enemy.monster).unwrap();
|
||||
(enemy_id, map_enemy_stats.exp)
|
||||
};
|
||||
})).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::RequestExp(RequestExp{
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::RequestExp(RequestExp {
|
||||
client: enemy_id as u8,
|
||||
target: 16,
|
||||
enemy_id: enemy_id as u16,
|
||||
client_id: 0,
|
||||
unused: 0,
|
||||
last_hitter: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::RequestExp(RequestExp{
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::RequestExp(RequestExp {
|
||||
client: enemy_id as u8,
|
||||
target: 16,
|
||||
enemy_id: enemy_id as u16,
|
||||
client_id: 0,
|
||||
unused: 0,
|
||||
last_hitter: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1 = ship.clients.get(&ClientId(1)).unwrap();
|
||||
let c2 = ship.clients.get(&ClientId(2)).unwrap();
|
||||
assert!(c1.character.exp == exp);
|
||||
assert!(c2.character.exp == (exp as f32 * 0.8) as u32);
|
||||
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
||||
assert!(client.character.exp == exp);
|
||||
})).await.unwrap();
|
||||
ship.clients.with(ClientId(2), |client| Box::pin(async move {
|
||||
assert!(client.character.exp == (exp as f32 * 0.8) as u32);
|
||||
})).await.unwrap();
|
||||
}
|
||||
|
@ -63,22 +63,22 @@ async fn test_equip_unit_from_equip_menu() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerEquipItem(PlayerEquipItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerEquipItem(PlayerEquipItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10001,
|
||||
sub_menu: 9,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
// case when someone tries to send invalid submenu? submenu is 9-12 in normal gameplay
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerEquipItem(PlayerEquipItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerEquipItem(PlayerEquipItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10002,
|
||||
sub_menu: 14,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let equips = entity_gateway.get_character_equips(&char1.id).await.unwrap();
|
||||
assert_eq!(equips.unit[0].unwrap(), item::ItemEntityId(2));
|
||||
@ -140,12 +140,12 @@ async fn test_unequip_armor_with_units() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUnequipItem(PlayerUnequipItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUnequipItem(PlayerUnequipItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let equips = entity_gateway.get_character_equips(&char1.id).await.unwrap();
|
||||
assert!(equips.armor.is_none());
|
||||
@ -213,13 +213,13 @@ async fn test_sort_items() {
|
||||
}).unwrap();
|
||||
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::SortItems(SortItems {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::SortItems(SortItems {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_ids: [0x10001u32, 0x10002, 0x10000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF],
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 3);
|
||||
|
@ -51,7 +51,7 @@ async fn test_pick_up_individual_item() {
|
||||
let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
|
||||
assert_eq!(p2_items.items.len(), 0);
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown1: 0,
|
||||
@ -60,20 +60,20 @@ async fn test_pick_up_individual_item() {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 0);
|
||||
let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
|
||||
assert_eq!(p2_items.items.len(), 0);
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 0);
|
||||
@ -129,7 +129,7 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown1: 0,
|
||||
@ -138,15 +138,15 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x210000,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 1);
|
||||
@ -188,7 +188,7 @@ async fn test_pick_up_item_stack_of_items_not_already_held() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown1: 0,
|
||||
@ -197,15 +197,15 @@ async fn test_pick_up_item_stack_of_items_not_already_held() {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x210000,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 1);
|
||||
@ -254,7 +254,7 @@ async fn test_pick_up_meseta_when_inventory_full() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
@ -262,22 +262,22 @@ async fn test_pick_up_meseta_when_inventory_full() {
|
||||
room: 0,
|
||||
x: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
amount: 23,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x00810001,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 30);
|
||||
@ -345,7 +345,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown1: 0,
|
||||
@ -354,15 +354,15 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x210000,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 30);
|
||||
@ -423,7 +423,7 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown1: 0,
|
||||
@ -432,28 +432,28 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x210000,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 30);
|
||||
let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
|
||||
assert_eq!(p2_items.items.len(), 0);
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x210000,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 30);
|
||||
@ -478,7 +478,7 @@ async fn test_can_not_drop_more_meseta_than_is_held() {
|
||||
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
@ -486,9 +486,9 @@ async fn test_can_not_drop_more_meseta_than_is_held() {
|
||||
room: 0,
|
||||
x: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let split_attempt = ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||
let split_attempt = ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
@ -545,7 +545,7 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown1: 0,
|
||||
@ -554,15 +554,15 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x210000,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
assert!(packets.len() == 0);
|
||||
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
@ -596,7 +596,7 @@ async fn test_can_not_pick_up_meseta_when_full() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
@ -604,22 +604,22 @@ async fn test_can_not_pick_up_meseta_when_full() {
|
||||
room: 0,
|
||||
x: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
amount: 23,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x00810001,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
assert!(packets.len() == 0);
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
@ -650,7 +650,7 @@ async fn test_meseta_caps_at_999999_when_trying_to_pick_up_more() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
@ -658,22 +658,22 @@ async fn test_meseta_caps_at_999999_when_trying_to_pick_up_more() {
|
||||
room: 0,
|
||||
x: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
amount: 23,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x00810001,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap();
|
||||
@ -714,7 +714,7 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
@ -722,22 +722,22 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
|
||||
room: 0,
|
||||
x: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 2,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10003,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 1);
|
||||
|
@ -43,11 +43,11 @@ async fn test_use_monomate() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 2);
|
||||
@ -90,16 +90,16 @@ async fn test_use_monomate_twice() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 2);
|
||||
@ -138,11 +138,11 @@ async fn test_use_last_monomate() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
@ -178,11 +178,11 @@ async fn test_use_nonstackable_tool() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 0);
|
||||
@ -219,21 +219,21 @@ async fn test_use_materials() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10001,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10001,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 2);
|
||||
|
@ -58,12 +58,12 @@ async fn test_mag_feed() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
for _ in 0..7usize {
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerFeedMag(PlayerFeedMag {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerFeedMag(PlayerFeedMag {
|
||||
client: 0,
|
||||
target: 0,
|
||||
mag_id: 0x10000,
|
||||
item_id: 0x10001,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
}
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
@ -115,7 +115,7 @@ async fn test_mag_change_owner() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown1: 0,
|
||||
@ -124,15 +124,15 @@ async fn test_mag_change_owner() {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 1);
|
||||
@ -198,11 +198,11 @@ async fn test_mag_cell() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10001,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
inventory_items.items[0].with_individual(|item| {
|
||||
|
@ -2,6 +2,7 @@ use elseware::common::serverstate::{ClientId, ServerState};
|
||||
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
||||
use elseware::entity::item;
|
||||
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
|
||||
use elseware::ship::location::RoomId;
|
||||
|
||||
use libpso::packet::ship::*;
|
||||
//use libpso::packet::messages::*;
|
||||
@ -63,11 +64,11 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
|
||||
join_lobby(&mut ship, ClientId(2)).await;
|
||||
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
let p = ship.handle(ClientId(2), &RecvShipPacket::MenuSelect(MenuSelect {
|
||||
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);
|
||||
})).await.unwrap();
|
||||
ship.handle(ClientId(2), RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap();
|
||||
|
||||
match &p[1].1 {
|
||||
SendShipPacket::AddToRoom(add_to) => {
|
||||
@ -81,10 +82,10 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
|
||||
|
||||
leave_room(&mut ship, ClientId(2)).await;
|
||||
|
||||
let p = ship.handle(ClientId(2), &RecvShipPacket::MenuSelect(MenuSelect {
|
||||
let p = ship.handle(ClientId(2), RecvShipPacket::MenuSelect(MenuSelect {
|
||||
menu: ROOM_MENU_ID,
|
||||
item: 0,
|
||||
})).await.unwrap().collect::<Vec<_>>();
|
||||
})).await.unwrap();
|
||||
|
||||
match &p[1].1 {
|
||||
SendShipPacket::AddToRoom(add_to) => {
|
||||
@ -108,12 +109,12 @@ async fn test_load_rare_monster_default_appear_rates() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
// assume episode 1
|
||||
let room = ship.blocks.0[0].rooms[0].as_ref().unwrap();
|
||||
println!("rare monster table: {:?}", room.rare_monster_table);
|
||||
let rates = &*room.rare_monster_table;
|
||||
for (_monster, rate) in rates.clone().appear_rate {
|
||||
assert_eq!(rate, 0.001953125f32); // 1/512 = 0.001953125
|
||||
}
|
||||
ship.blocks.0[0].rooms.with(RoomId(0), |room| Box::pin(async move {
|
||||
let rates = &*room.rare_monster_table;
|
||||
for (_monster, rate) in rates.clone().appear_rate {
|
||||
assert_eq!(rate, 0.001953125f32); // 1/512 = 0.001953125
|
||||
}
|
||||
})).await.unwrap();
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
@ -127,7 +128,7 @@ async fn test_set_valid_quest_group() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::RequestQuestList(RequestQuestList{flag: 0})).await.unwrap().collect::<Vec<_>>();
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::RequestQuestList(RequestQuestList{flag: 0})).await.unwrap();
|
||||
match &packets[0].1 {
|
||||
SendShipPacket::QuestCategoryList(quest_cat) => {
|
||||
assert!(String::from_utf16_lossy(&quest_cat.quest_categories[0].name).starts_with("Retrieval"));
|
||||
@ -147,7 +148,7 @@ async fn test_set_invalid_quest_group() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::RequestQuestList(RequestQuestList{flag: 100})).await.unwrap().collect::<Vec<_>>();
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::RequestQuestList(RequestQuestList{flag: 100})).await.unwrap();
|
||||
match &packets[0].1 {
|
||||
SendShipPacket::QuestCategoryList(quest_cat) => {
|
||||
// flag > quest category length should take the highest value allowed for quest category which is 1 in multimode (for govt quests) and 0 in other modes.
|
||||
@ -174,7 +175,7 @@ async fn test_get_room_info() {
|
||||
join_lobby(&mut ship, ClientId(2)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
let _expectedmsg = String::from("1 Lv1 GODmar\nHUmar Pioneer 2\n");
|
||||
let packets = ship.handle(ClientId(2), &RecvShipPacket::MenuDetail(MenuDetail{menu: 3, item: 0})).await.unwrap().collect::<Vec<_>>();
|
||||
let packets = ship.handle(ClientId(2), RecvShipPacket::MenuDetail(MenuDetail{menu: 3, item: 0})).await.unwrap();
|
||||
assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::SmallLeftDialog(SmallLeftDialog{
|
||||
padding: [17664, 1157645568],
|
||||
msg: _expectedmsg,
|
||||
@ -196,7 +197,7 @@ async fn test_cannot_get_room_info_after_room_is_closed() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
leave_room(&mut ship, ClientId(1)).await;
|
||||
let _expectedmsg = String::from("Game is no longer active!\0");
|
||||
let packets = ship.handle(ClientId(2), &RecvShipPacket::MenuDetail(MenuDetail{menu: 3, item: 0})).await.unwrap().collect::<Vec<_>>();
|
||||
let packets = ship.handle(ClientId(2), RecvShipPacket::MenuDetail(MenuDetail{menu: 3, item: 0})).await.unwrap();
|
||||
assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::SmallLeftDialog(SmallLeftDialog{
|
||||
padding: [17664, 1157645568],
|
||||
msg: _expectedmsg,
|
||||
@ -218,7 +219,7 @@ async fn test_cannot_join_room_after_its_closed() {
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
leave_room(&mut ship, ClientId(1)).await;
|
||||
let _expectedmsg = String::from("This room no longer exists!\0");
|
||||
let packets = ship.handle(ClientId(2), &RecvShipPacket::MenuSelect(MenuSelect{menu: 3, item: 0})).await.unwrap().collect::<Vec<_>>();
|
||||
let packets = ship.handle(ClientId(2), RecvShipPacket::MenuSelect(MenuSelect{menu: 3, item: 0})).await.unwrap();
|
||||
assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::SmallDialog(SmallDialog{
|
||||
padding: [0,0],
|
||||
msg: _expectedmsg, // wow yes cool rust is so great literally the best i can't put a String::from() directly in here.
|
||||
|
@ -1,7 +1,7 @@
|
||||
use elseware::common::serverstate::{ClientId, ServerState};
|
||||
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
||||
use elseware::entity::item;
|
||||
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
|
||||
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError};
|
||||
use elseware::ship::room::Difficulty;
|
||||
use elseware::ship::items::state::ItemStateError;
|
||||
|
||||
@ -27,11 +27,11 @@ async fn test_player_opens_weapon_shop() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 1
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
match &packets[0].1 {
|
||||
@ -57,11 +57,11 @@ async fn test_player_opens_tool_shop() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 0
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
match &packets[0].1 {
|
||||
@ -87,11 +87,11 @@ async fn test_player_opens_armor_shop() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 2
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
match &packets[0].1 {
|
||||
@ -118,12 +118,12 @@ async fn test_player_buys_from_weapon_shop() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 1
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10000,
|
||||
@ -131,7 +131,7 @@ async fn test_player_buys_from_weapon_shop() {
|
||||
shop_index: 0,
|
||||
amount: 1,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert!(c1_meseta.0 < 999999);
|
||||
@ -156,12 +156,12 @@ async fn test_player_buys_from_tool_shop() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10000,
|
||||
@ -169,7 +169,7 @@ async fn test_player_buys_from_tool_shop() {
|
||||
shop_index: 0,
|
||||
amount: 1,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert!(c1_meseta.0 < 999999);
|
||||
@ -193,12 +193,12 @@ async fn test_player_buys_multiple_from_tool_shop() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10000,
|
||||
@ -206,7 +206,7 @@ async fn test_player_buys_multiple_from_tool_shop() {
|
||||
shop_index: 0,
|
||||
amount: 5,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 999749);
|
||||
@ -234,12 +234,12 @@ async fn test_player_buys_from_armor_shop() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 2
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10000,
|
||||
@ -247,7 +247,7 @@ async fn test_player_buys_from_armor_shop() {
|
||||
shop_index: 0,
|
||||
amount: 1,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert!(c1_meseta.0 < 999999);
|
||||
@ -288,12 +288,12 @@ async fn test_player_sells_3_attr_weapon_to_shop() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 4406);
|
||||
@ -319,12 +319,12 @@ async fn test_other_clients_see_purchase() {
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Normal).await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 1
|
||||
})))).await.unwrap().for_each(drop);
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10000,
|
||||
@ -332,7 +332,7 @@ async fn test_other_clients_see_purchase() {
|
||||
shop_index: 0,
|
||||
amount: 1,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_eq!(packets[0].0, ClientId(2));
|
||||
@ -370,12 +370,12 @@ async fn test_other_clients_see_stacked_purchase() {
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Normal).await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 1
|
||||
})))).await.unwrap().for_each(drop);
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10000,
|
||||
@ -383,7 +383,7 @@ async fn test_other_clients_see_stacked_purchase() {
|
||||
shop_index: 0,
|
||||
amount: 1,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_eq!(packets[0].0, ClientId(2));
|
||||
@ -406,12 +406,12 @@ async fn test_buying_item_without_enough_mseseta() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Normal).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 1
|
||||
})))).await.unwrap().for_each(drop);
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10000,
|
||||
@ -444,12 +444,12 @@ async fn test_player_double_buys_from_tool_shop() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10000,
|
||||
@ -457,8 +457,8 @@ async fn test_player_double_buys_from_tool_shop() {
|
||||
shop_index: 0,
|
||||
amount: 3,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10001,
|
||||
@ -466,8 +466,8 @@ async fn test_player_double_buys_from_tool_shop() {
|
||||
shop_index: 1,
|
||||
amount: 2,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10002,
|
||||
@ -475,7 +475,7 @@ async fn test_player_double_buys_from_tool_shop() {
|
||||
shop_index: 0,
|
||||
amount: 4,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert!(c1_meseta.0 < 999999);
|
||||
@ -509,11 +509,11 @@ async fn test_techs_disappear_from_shop_when_bought() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 0,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
let first_tech = match &packets[0].1 {
|
||||
SendShipPacket::Message(Message {msg: GameMessage::ShopList(shop_list)}) => {
|
||||
@ -527,7 +527,7 @@ async fn test_techs_disappear_from_shop_when_bought() {
|
||||
_ => panic!(""),
|
||||
};
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10000,
|
||||
@ -535,8 +535,8 @@ async fn test_techs_disappear_from_shop_when_bought() {
|
||||
shop_index: first_tech as u8,
|
||||
amount: 1,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10001,
|
||||
@ -544,7 +544,7 @@ async fn test_techs_disappear_from_shop_when_bought() {
|
||||
shop_index: first_tech as u8,
|
||||
amount: 1,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
p1_items.items[0].with_individual(|item1| {
|
||||
@ -571,11 +571,11 @@ async fn test_units_disappear_from_shop_when_bought() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
|
||||
|
||||
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
let packets = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
|
||||
client: 255,
|
||||
target: 255,
|
||||
shop_type: 2,
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
})))).await.unwrap();
|
||||
|
||||
let first_unit = match &packets[0].1 {
|
||||
SendShipPacket::Message(Message {msg: GameMessage::ShopList(shop_list)}) => {
|
||||
@ -589,7 +589,7 @@ async fn test_units_disappear_from_shop_when_bought() {
|
||||
_ => panic!(""),
|
||||
};
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10000,
|
||||
@ -597,8 +597,8 @@ async fn test_units_disappear_from_shop_when_bought() {
|
||||
shop_index: first_unit as u8,
|
||||
amount: 1,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
})))).await.unwrap();
|
||||
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
|
||||
client: 255,
|
||||
target: 255,
|
||||
item_id: 0x10001,
|
||||
@ -606,7 +606,7 @@ async fn test_units_disappear_from_shop_when_bought() {
|
||||
shop_index: first_unit as u8,
|
||||
amount: 1,
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
p1_items.items[0].with_individual(|item1| {
|
||||
@ -649,12 +649,12 @@ async fn test_player_sells_untekked_weapon() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 1);
|
||||
@ -693,12 +693,12 @@ async fn test_player_sells_rare_item() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 10);
|
||||
@ -736,12 +736,12 @@ async fn test_player_sells_partial_photon_drop_stack() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 3,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 3000);
|
||||
@ -777,12 +777,12 @@ async fn test_player_sells_basic_frame() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 24);
|
||||
@ -818,12 +818,12 @@ async fn test_player_sells_max_frame() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 74);
|
||||
@ -858,12 +858,12 @@ async fn test_player_sells_basic_barrier() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 69);
|
||||
@ -898,12 +898,12 @@ async fn test_player_sells_max_barrier() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 122);
|
||||
@ -937,12 +937,12 @@ async fn test_player_sells_1_star_minusminus_unit() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 125);
|
||||
@ -976,12 +976,12 @@ async fn test_player_sells_5_star_plusplus_unit() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 625);
|
||||
@ -1017,12 +1017,12 @@ async fn test_player_sells_rare_frame() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 10);
|
||||
@ -1057,12 +1057,12 @@ async fn test_player_sells_rare_barrier() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 10);
|
||||
@ -1096,12 +1096,12 @@ async fn test_player_sells_rare_unit() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
})))).await.unwrap();
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 10);
|
||||
@ -1136,13 +1136,14 @@ async fn test_player_cant_sell_if_meseta_would_go_over_max() {
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
|
||||
let ack = ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
let ack = ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
amount: 1,
|
||||
})))).await.err().unwrap();
|
||||
assert!(matches!(ack.downcast::<ItemStateError>().unwrap(), ItemStateError::FullOfMeseta));
|
||||
//assert_eq!(ack, ShipError::ItemStateError(ItemStateError::FullOfMeseta));
|
||||
assert!(matches!(ack.downcast::<ShipError>().unwrap(), ShipError::ItemStateError(ItemStateError::FullOfMeseta)));
|
||||
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 999995);
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user