Browse Source

Merge pull request 'interserver communication' (#202) from interserver_communication into master

pbs
jake 4 years ago
parent
commit
431752cbf7
  1. 51
      src/bin/main.rs
  2. 54
      src/common/interserver.rs
  3. 92
      src/common/mainloop/client.rs
  4. 220
      src/common/mainloop/interserver.rs
  5. 45
      src/common/mainloop/mod.rs
  6. 1
      src/common/mod.rs
  7. 1
      src/common/serverstate.rs
  8. 3
      src/entity/character.rs
  9. 66
      src/login/character.rs
  10. 2
      src/login/login.rs
  11. 94
      src/ship/ship.rs
  12. 104
      tests/test_bank.rs
  13. 16
      tests/test_exp_gain.rs
  14. 40
      tests/test_item_pickup.rs
  15. 20
      tests/test_item_use.rs

51
src/bin/main.rs

@ -1,3 +1,4 @@
use std::net::Ipv4Addr;
use std::time::SystemTime; use std::time::SystemTime;
use log::{info}; use log::{info};
@ -5,12 +6,14 @@ use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config,
use elseware::login::login::LoginServerState; use elseware::login::login::LoginServerState;
use elseware::login::character::CharacterServerState; use elseware::login::character::CharacterServerState;
use elseware::ship::ship::ShipServerState; use elseware::ship::ship::ShipServerState;
use elseware::ship::ship::ShipServerStateBuilder;
use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity}; use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::character::NewCharacterEntity; use elseware::entity::character::NewCharacterEntity;
use elseware::entity::item::{NewItemEntity, ItemDetail, ItemLocation}; use elseware::entity::item::{NewItemEntity, ItemDetail, ItemLocation};
use elseware::entity::item; use elseware::entity::item;
use elseware::common::mainloop::*;
fn setup_logger() { fn setup_logger() {
let colors = fern::colors::ColoredLevelConfig::new() let colors = fern::colors::ColoredLevelConfig::new()
@ -185,39 +188,51 @@ fn main() {
}).await; }).await;
} }
let patch = async_std::task::spawn(async {
info!("[patch] starting server"); info!("[patch] starting server");
let patch_config = load_config(); let patch_config = load_config();
let patch_motd = load_motd(); let patch_motd = load_motd();
let (patch_file_tree, patch_file_lookup) = generate_patch_tree(patch_config.path.as_str()); 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_state = PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd);
elseware::common::mainloop::mainloop_async(patch_state, patch_config.port).await;
});
let patch_loop = patch_mainloop(patch_state, patch_config.port);
let thread_entity_gateway = entity_gateway.clone(); let thread_entity_gateway = entity_gateway.clone();
let auth = async_std::task::spawn(async {
info!("[auth] starting server"); info!("[auth] starting server");
let auth_state = LoginServerState::new(thread_entity_gateway);
elseware::common::mainloop::mainloop_async(auth_state, elseware::login::login::LOGIN_PORT).await;
});
let login_state = LoginServerState::new(thread_entity_gateway);
let login_loop = login_mainloop(login_state, elseware::login::login::LOGIN_PORT);
let thread_entity_gateway = entity_gateway.clone(); let thread_entity_gateway = entity_gateway.clone();
let character = async_std::task::spawn(async {
info!("[character] starting server"); info!("[character] starting server");
let char_state = CharacterServerState::new(thread_entity_gateway); let char_state = CharacterServerState::new(thread_entity_gateway);
elseware::common::mainloop::mainloop_async(char_state, elseware::login::character::CHARACTER_PORT).await;
});
let character_loop = character_mainloop(char_state, elseware::login::character::CHARACTER_PORT, elseware::login::login::COMMUNICATION_PORT);
let thread_entity_gateway = entity_gateway.clone(); let thread_entity_gateway = entity_gateway.clone();
let ship = async_std::task::spawn(async {
info!("[ship] starting server"); info!("[ship] starting server");
let ship_state = ShipServerState::new(thread_entity_gateway);
elseware::common::mainloop::mainloop_async(ship_state, elseware::ship::ship::SHIP_PORT).await;
});
let ship_state = ShipServerStateBuilder::new()
.name("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);
let thread_entity_gateway = entity_gateway.clone();
let ship_state = ShipServerStateBuilder::new()
.name("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);
let thread_entity_gateway = entity_gateway.clone();
let ship_state = ShipServerStateBuilder::new()
.name("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);
futures::join!(patch, auth, character, ship);
futures::future::join_all(vec![patch_loop, login_loop, character_loop, ship_loop, ship_loop2, ship_loop3]).await;
}); });
} }

54
src/common/interserver.rs

@ -0,0 +1,54 @@
use std::net::Ipv4Addr;
use serde::{Serialize, Deserialize};
use serde::de::DeserializeOwned;
use crate::entity::character::CharacterEntityId;
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ServerId(pub usize);
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthToken(pub String);
#[derive(Debug, Serialize, Deserialize)]
pub struct Ship {
pub name: String,
pub ip: Ipv4Addr,
pub port: u16,
pub block_count: u32,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum LoginMessage {
SendMail {
character_id: CharacterEntityId,
title: String,
message: String,
},
ShipList {
ships: Vec<Ship>,
}
}
#[derive(Debug, Serialize, Deserialize)]
pub enum ShipMessage {
Authenticate(AuthToken),
NewShip(Ship),
SendMail {
character_id: CharacterEntityId,
title: String,
message: String,
},
RequestShipList,
}
#[async_trait::async_trait]
pub trait InterserverActor {
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_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)>;
}

92
src/common/mainloop.rs → src/common/mainloop/client.rs

@ -1,4 +1,5 @@
#![allow(dead_code)]
use std::pin::Pin;
use futures::future::Future;
use log::{trace, info, warn}; use log::{trace, info, warn};
use async_std::sync::{Arc, Mutex}; use async_std::sync::{Arc, Mutex};
use async_std::io::prelude::{ReadExt, WriteExt}; use async_std::io::prelude::{ReadExt, WriteExt};
@ -118,6 +119,7 @@ async fn send_pkt<S: SendServerPacket + Send + std::fmt::Debug>(socket: Arc<asyn
-> Result<(), NetworkError> -> Result<(), NetworkError>
{ {
let buf = pkt.as_bytes(); let buf = pkt.as_bytes();
println!("sndbuf: {:?}", buf);
let cbuf = cipher.lock().await.encrypt(&buf)?; let cbuf = cipher.lock().await.encrypt(&buf)?;
let mut ssock = &*socket; let mut ssock = &*socket;
ssock.write_all(&cbuf).await?; ssock.write_all(&cbuf).await?;
@ -200,7 +202,8 @@ async fn client_recv_loop<S, R>(client_id: ClientId,
socket: Arc<async_std::net::TcpStream>, socket: Arc<async_std::net::TcpStream>,
cipher: Arc<Mutex<Box<dyn PSOCipher + Send>>>, cipher: Arc<Mutex<Box<dyn PSOCipher + Send>>>,
server_sender: async_std::sync::Sender<ClientAction<ServerStateAction<S>, R>>, server_sender: async_std::sync::Sender<ClientAction<ServerStateAction<S>, R>>,
client_sender: async_std::sync::Sender<ServerStateAction<S>>) where
client_sender: async_std::sync::Sender<ServerStateAction<S>>)
where
S: SendServerPacket + std::fmt::Debug + Send + 'static, S: SendServerPacket + std::fmt::Debug + Send + 'static,
R: RecvServerPacket + std::fmt::Debug + Send + 'static, R: RecvServerPacket + std::fmt::Debug + Send + 'static,
{ {
@ -237,9 +240,8 @@ async fn client_send_loop<S>(client_id: ClientId,
socket: Arc<async_std::net::TcpStream>, socket: Arc<async_std::net::TcpStream>,
cipher_in: Arc<Mutex<Box<dyn PSOCipher + Send>>>, cipher_in: Arc<Mutex<Box<dyn PSOCipher + Send>>>,
cipher_out: Arc<Mutex<Box<dyn PSOCipher + Send>>>, cipher_out: Arc<Mutex<Box<dyn PSOCipher + Send>>>,
client_receiver: async_std::sync::Receiver<ServerStateAction<S>>,
) where
client_receiver: async_std::sync::Receiver<ServerStateAction<S>>)
where
S: SendServerPacket + std::fmt::Debug + Send + 'static, S: SendServerPacket + std::fmt::Debug + Send + 'static,
{ {
async_std::task::spawn(async move { async_std::task::spawn(async move {
@ -264,26 +266,85 @@ async fn client_send_loop<S>(client_id: ClientId,
}); });
} }
async fn state_client_loop<STATE, S, R, E>(state: Arc<Mutex<STATE>>,
server_state_receiver: async_std::sync::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) => {
clients.insert(client_id, sender.clone());
for action in state.on_connect(client_id) {
match action {
OnConnect::Cipher((inc, outc)) => {
sender.send(ServerStateAction::Cipher(inc, outc)).await;
},
OnConnect::Packet(pkt) => {
sender.send(ServerStateAction::Packet(pkt)).await;
}
}
}
},
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;
}
}
},
Err(err) => {
warn!("[client {:?} state handler error] {:?}", client_id, err);
}
}
},
ClientAction::Disconnect(client_id) => {
let pkts = state.on_disconnect(client_id);
for (client_id, pkt) in pkts {
if let Some(client) = clients.get_mut(&client_id) {
client.send(ServerStateAction::Packet(pkt)).await;
}
}
if let Some(client) = clients.get_mut(&client_id) {
client.send(ServerStateAction::Disconnect).await;
}
}
}
}
});
}
pub async fn mainloop_async<STATE, S, R, E>(state: STATE, port: u16) where
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, STATE: ServerState<SendPacket=S, RecvPacket=R, PacketError=E> + Send + 'static,
S: SendServerPacket + std::fmt::Debug + Send + Sync + 'static, S: SendServerPacket + std::fmt::Debug + Send + Sync + 'static,
R: RecvServerPacket + std::fmt::Debug + Send + Sync + 'static, R: RecvServerPacket + std::fmt::Debug + Send + Sync + 'static,
E: std::fmt::Debug + Send, E: std::fmt::Debug + Send,
{ {
let listener = 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 = 1;
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::sync::channel(1024); let (server_state_sender, server_state_receiver) = async_std::sync::channel(1024);
server_state_loop(state, server_state_receiver).await;
state_client_loop(state, server_state_receiver).await;
loop { loop {
let (sock, addr) = listener.accept().await.unwrap(); let (sock, addr) = listener.accept().await.unwrap();
let client_id = crate::common::serverstate::ClientId(id);
id += 1; id += 1;
let client_id = crate::common::serverstate::ClientId(id);
info!("new client {:?} {:?} {:?}", client_id, sock, addr); info!("new client {:?} {:?} {:?}", client_id, sock, addr);
@ -295,7 +356,6 @@ pub async fn mainloop_async<STATE, S, R, E>(state: STATE, port: u16) where
client_recv_loop(client_id, socket.clone(), cipher_in.clone(), server_state_sender.clone(), client_sender).await; client_recv_loop(client_id, socket.clone(), cipher_in.clone(), server_state_sender.clone(), client_sender).await;
client_send_loop(client_id, socket.clone(), cipher_in.clone(), cipher_out.clone(), client_receiver).await; client_send_loop(client_id, socket.clone(), cipher_in.clone(), cipher_out.clone(), client_receiver).await;
} }
});
listener.await
}))
} }

220
src/common/mainloop/interserver.rs

@ -0,0 +1,220 @@
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::io::prelude::{ReadExt, WriteExt};
use std::collections::HashMap;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::common::interserver::{ServerId, InterserverActor};
use crate::login::character::CharacterServerState;
use crate::ship::ship::ShipServerState;
use crate::entity::gateway::entitygateway::EntityGateway;
#[derive(Debug)]
enum MessageReceiverError {
//InvalidSize,
InvalidPayload,
//NetworkError(std::io::Error),
Disconnected,
}
struct MessageReceiver {
socket: async_std::net::TcpStream,
}
impl MessageReceiver {
fn new(socket: async_std::net::TcpStream) -> MessageReceiver {
MessageReceiver {
socket: socket,
}
}
async fn recv<R: DeserializeOwned + std::fmt::Debug + Send>(&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;
let mut payload = vec![0u8; size];
self.socket.read_exact(&mut payload).await.map_err(|err| MessageReceiverError::Disconnected)?;
let payload = String::from_utf8(payload).map_err(|_| MessageReceiverError::InvalidPayload)?;
let msg = serde_json::from_str(&payload).map_err(|_| MessageReceiverError::InvalidPayload)?;
Ok(msg)
}
}
#[derive(Debug)]
enum InterserverInputAction<S, R> {
NewConnection(ServerId, async_std::sync::Sender<S>),
Message(ServerId, R),
Disconnect(ServerId),
}
async fn interserver_state_loop<A, S, R>(state: Arc<Mutex<A>>, action_receiver: async_std::sync::Receiver<InterserverInputAction<S, R>>)
where
A: InterserverActor<SendMessage=S, RecvMessage=R, Error=()> + Send + 'static,
S: Serialize + Send + 'static,
R: DeserializeOwned + Send + 'static,
{
async_std::task::spawn(async move {
let mut ships = HashMap::new();
loop {
info!("interserver loop");
let action = action_receiver.recv().await.unwrap();
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;
}
}
},
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;
}
}
},
Err(err) => {
warn!("[server {:?} state handler error] {:?}", server_id, err);
}
}
},
InterserverInputAction::Disconnect(server_id) => {
let actions = state.on_disconnect(server_id).await;
for (server, action) in actions {
if let Some(sender) = ships.get_mut(&server) {
sender.send(action).await;
}
}
break;
}
}
}
});
}
async fn login_recv_loop<S, R>(server_id: ServerId,
socket: async_std::net::TcpStream,
state_loop_sender: async_std::sync::Sender<InterserverInputAction<S, R>>,
output_loop_sender: async_std::sync::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;
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
},
Err(err) => {
if let MessageReceiverError::Disconnected = err {
info!("[login recv loop disconnect] {:?}", server_id);
state_loop_sender.send(InterserverInputAction::Disconnect(server_id)).await;
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::sync::Receiver<S>)
where
S: Serialize + std::fmt::Debug + Send + 'static,
{
async_std::task::spawn(async move {
loop {
info!("login send loop");
let msg = output_loop_receiver.recv().await.unwrap();
let payload = serde_json::to_string(&msg);
if let Ok(payload) = payload {
let len_bytes = u32::to_le_bytes(payload.len() as u32);
match socket.write_all(&len_bytes).await {
Ok(_) => {},
Err(err) => warn!("send failed: {:?}", err),
}
match socket.write_all(&payload.as_bytes()).await {
Ok(_) => {},
Err(err) => warn!("send failed: {:?}", err),
}
}
}
});
}
pub fn login_listen_mainloop<EG: EntityGateway + '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::sync::channel(1024);
interserver_state_loop(state, 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::sync::channel(64);
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 + '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::sync::channel(1024);
interserver_state_loop(state, server_state_receiver).await;
loop {
let socket = async_std::net::TcpStream::connect((ip, port)).await.unwrap();
id += 1;
let server_id = crate::common::interserver::ServerId(id);
let (client_sender, client_receiver) = async_std::sync::channel(64);
info!("ship connected to login: {:?}", socket);
login_recv_loop(server_id, socket.clone(), server_state_sender.clone(), client_sender).await;
interserver_send_loop(server_id, socket.clone(), client_receiver).await;
loop {
if let Err(_) = socket.peer_addr() {
info!("ship connected to login: {:?}", socket);
break;
}
async_std::task::sleep(Duration::from_secs(10)).await;
}
}
}))
}

45
src/common/mainloop/mod.rs

@ -0,0 +1,45 @@
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 + '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.clone(), login_port);
Box::pin(client_mainloop)
}
pub fn character_mainloop<EG: EntityGateway + '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.clone(), comm_port);
Box::pin(join_all(vec![client_mainloop, ship_communication_mainloop]).map(|_| ()))
}
pub fn ship_mainloop<EG: EntityGateway + '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.clone(), comm_ip, comm_port);
Box::pin(join_all(vec![client_mainloop, login_communication_mainloop]).map(|_| ()))
}

1
src/common/mod.rs

@ -2,6 +2,7 @@ pub mod cipherkeys;
pub mod serverstate; pub mod serverstate;
pub mod mainloop; pub mod mainloop;
pub mod leveltable; pub mod leveltable;
pub mod interserver;
// https://www.reddit.com/r/rust/comments/33xhhu/how_to_create_an_array_of_structs_that_havent/ // https://www.reddit.com/r/rust/comments/33xhhu/how_to_create_an_array_of_structs_that_havent/
#[macro_export] #[macro_export]

1
src/common/serverstate.rs

@ -17,6 +17,7 @@ pub trait SendServerPacket: Sized + Sync {
fn as_bytes(&self) -> Vec<u8>; fn as_bytes(&self) -> Vec<u8>;
} }
// TODO: rename this trait, this isn't the state but the actionability of the state re: the client
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait ServerState { pub trait ServerState {
type SendPacket: SendServerPacket; type SendPacket: SendServerPacket;

3
src/entity/character.rs

@ -1,5 +1,6 @@
use std::convert::{From, Into}; use std::convert::{From, Into};
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use libpso::packet::ship::{UpdateConfig, WriteInfoboard}; use libpso::packet::ship::{UpdateConfig, WriteInfoboard};
use libpso::character::character::{DEFAULT_PALETTE_CONFIG, DEFAULT_TECH_MENU}; use libpso::character::character::{DEFAULT_PALETTE_CONFIG, DEFAULT_TECH_MENU};
@ -235,7 +236,7 @@ pub struct CharacterMaterials {
pub tp: u32, pub tp: u32,
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct CharacterEntityId(pub u32); pub struct CharacterEntityId(pub u32);
#[derive(Clone)] #[derive(Clone)]

66
src/login/character.rs

@ -1,6 +1,6 @@
#![allow(dead_code, unused_assignments)] #![allow(dead_code, unused_assignments)]
use std::io::Read; use std::io::Read;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use rand::Rng; use rand::Rng;
use crc::{crc32, Hasher32}; use crc::{crc32, Hasher32};
@ -13,6 +13,7 @@ use libpso::character::character;
use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; 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::CharacterLevelTable;
use libpso::{utf8_to_array, utf8_to_utf16_array}; use libpso::{utf8_to_array, utf8_to_utf16_array};
@ -162,37 +163,17 @@ impl ClientState {
} }
struct Ship {
flags: u32,
name: String,
ip: [u8; 4],
port: u16,
}
impl Ship {
fn new(name: &str, ip: [u8; 4], port: u16) -> Ship {
Ship {
flags: 0,
name: name.to_owned(),
ip: ip,
port: port,
}
}
}
pub struct CharacterServerState<EG: EntityGateway> { pub struct CharacterServerState<EG: EntityGateway> {
entity_gateway: EG, entity_gateway: EG,
param_header: ParamDataHeader, param_header: ParamDataHeader,
param_data: Vec<u8>, param_data: Vec<u8>,
clients: HashMap<ClientId, ClientState>, clients: HashMap<ClientId, ClientState>,
ships: Vec<Ship>,
ships: BTreeMap<ServerId, Ship>,
level_table: CharacterLevelTable, level_table: CharacterLevelTable,
} }
async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccountEntity, preview: &CharacterPreview) { async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccountEntity, preview: &CharacterPreview) {
//let mut character = entity_gateway.new_character_by_user(&user);
//new_character_from_preview(&mut char, preview);
let mut character = new_character_from_preview(user, preview); let mut character = new_character_from_preview(user, preview);
match character.char_class { match character.char_class {
CharacterClass::FOmar | CharacterClass::FOmarl| CharacterClass::FOnewm | CharacterClass::FOnewearl => character.techs.set_tech(Technique::Foie, TechLevel(1)), CharacterClass::FOmar | CharacterClass::FOmarl| CharacterClass::FOnewm | CharacterClass::FOnewearl => character.techs.set_tech(Technique::Foie, TechLevel(1)),
@ -288,21 +269,16 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
} }
impl<EG: EntityGateway> CharacterServerState<EG> { impl<EG: EntityGateway> CharacterServerState<EG> {
pub fn new(entity_gateway: EG) -> CharacterServerState<EG> { pub fn new(entity_gateway: EG) -> CharacterServerState<EG> {
let (param_header, param_data) = generate_param_data("data/param/"); let (param_header, param_data) = generate_param_data("data/param/");
let ships = vec![Ship::new("Sona-Nyl", [127,0,0,1], 23423),
Ship::new("Dylath-Leen", [127,0,0,1], 23424),
Ship::new("Thalarion", [127,0,0,1], 23425),
];
CharacterServerState { CharacterServerState {
entity_gateway: entity_gateway, entity_gateway: entity_gateway,
param_header: param_header, param_header: param_header,
param_data: param_data, param_data: param_data,
clients: HashMap::new(), clients: HashMap::new(),
ships: ships,
ships: BTreeMap::new(),
level_table: CharacterLevelTable::new(), level_table: CharacterLevelTable::new(),
} }
} }
@ -326,10 +302,10 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
fn send_ship_list(&mut self, _id: ClientId, _pkt: &Login) -> Result<Vec<SendCharacterPacket>, CharacterError> { fn send_ship_list(&mut self, _id: ClientId, _pkt: &Login) -> Result<Vec<SendCharacterPacket>, CharacterError> {
Ok(vec![SendCharacterPacket::Timestamp(Timestamp::new(chrono::Utc::now())), Ok(vec![SendCharacterPacket::Timestamp(Timestamp::new(chrono::Utc::now())),
SendCharacterPacket::ShipList(ShipList::new(self.ships.iter().enumerate().map(|(i, s)| {
SendCharacterPacket::ShipList(ShipList::new(self.ships.iter().map(|(i, s)| {
ShipListEntry { ShipListEntry {
menu: SHIP_MENU_ID, menu: SHIP_MENU_ID,
item: i as u32,
item: i.0 as u32,
flags: 0, flags: 0,
name: utf8_to_utf16_array!(s.name, 0x11) name: utf8_to_utf16_array!(s.name, 0x11)
} }
@ -489,9 +465,9 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
return Err(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item)); return Err(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item));
} }
let ship = self.ships.get(menuselect.item as usize)
let ship = self.ships.get(&ServerId(menuselect.item as usize))
.ok_or(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?; .ok_or(CharacterError::InvalidMenuSelection(menuselect.menu, menuselect.item))?;
Ok(vec![SendCharacterPacket::RedirectClient(RedirectClient::new(u32::from_le_bytes(ship.ip), ship.port))])
Ok(vec![SendCharacterPacket::RedirectClient(RedirectClient::new(u32::from_le_bytes(ship.ip.octets()), ship.port))])
} }
} }
@ -566,6 +542,32 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
} }
} }
#[async_trait::async_trait]
impl<EG: EntityGateway> InterserverActor for CharacterServerState<EG> {
type SendMessage = LoginMessage;
type RecvMessage = ShipMessage;
type Error = ();
async fn on_connect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
Vec::new()
}
async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
match msg {
ShipMessage::Authenticate(_auth_token) => {},
ShipMessage::NewShip(new_ship) => {
self.ships.insert(id, new_ship);
},
_ => {}
}
Ok(Vec::new())
}
async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
Vec::new()
}
}
fn new_character_from_preview(user: &UserAccountEntity, preview: &CharacterPreview) -> NewCharacterEntity { fn new_character_from_preview(user: &UserAccountEntity, preview: &CharacterPreview) -> NewCharacterEntity {
let mut character = NewCharacterEntity::new(user.id); let mut character = NewCharacterEntity::new(user.id);

2
src/login/login.rs

@ -17,6 +17,7 @@ use crate::entity::gateway::EntityGateway;
use crate::entity::account::{UserAccountEntity}; use crate::entity::account::{UserAccountEntity};
pub const LOGIN_PORT: u16 = 12000; pub const LOGIN_PORT: u16 = 12000;
pub const COMMUNICATION_PORT: u16 = 12123;
#[derive(Debug)] #[derive(Debug)]
pub enum LoginError { pub enum LoginError {
@ -138,6 +139,7 @@ impl<EG: EntityGateway> ServerState for LoginServerState<EG> {
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::time::SystemTime; use std::time::SystemTime;

94
src/ship/ship.rs

@ -1,4 +1,5 @@
#![allow(dead_code, unused_must_use)] #![allow(dead_code, unused_must_use)]
use std::net::Ipv4Addr;
use std::collections::HashMap; use std::collections::HashMap;
use rand::Rng; use rand::Rng;
@ -15,6 +16,7 @@ use libpso::packet::ship::{BLOCK_MENU_ID, ROOM_MENU_ID};
use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
use crate::common::leveltable::CharacterLevelTable; use crate::common::leveltable::CharacterLevelTable;
use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage};
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use crate::entity::account::{UserAccountEntity, UserSettingsEntity}; use crate::entity::account::{UserAccountEntity, UserSettingsEntity};
@ -246,6 +248,58 @@ impl ClientState {
} }
} }
pub struct ShipServerStateBuilder<EG: EntityGateway> {
entity_gateway: Option<EG>,
name: Option<String>,
ip: Option<Ipv4Addr>,
port: Option<u16>,
}
impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
pub fn new() -> ShipServerStateBuilder<EG> {
ShipServerStateBuilder {
entity_gateway: None,
name: None,
ip: None,
port: None,
}
}
pub fn gateway(mut self, entity_gateway: EG) -> ShipServerStateBuilder<EG> {
self.entity_gateway = Some(entity_gateway);
self
}
pub fn name(mut self, name: String) -> ShipServerStateBuilder<EG> {
self.name = Some(name);
self
}
pub fn ip(mut self, ip: Ipv4Addr) -> ShipServerStateBuilder<EG> {
self.ip = Some(ip);
self
}
pub fn port(mut self, port: u16) -> ShipServerStateBuilder<EG> {
self.port = Some(port);
self
}
pub fn build(self) -> ShipServerState<EG> {
ShipServerState {
entity_gateway: self.entity_gateway.unwrap(),
clients: HashMap::new(),
client_location: ClientLocation::new(),
level_table: CharacterLevelTable::new(),
name: self.name.unwrap_or("NAMENOTSET".into()),
rooms: [None; MAX_ROOMS],
item_manager: items::ItemManager::new(),
quests: quests::load_quests("data/quests.toml".into()).unwrap(),
ip: self.ip.unwrap_or(Ipv4Addr::new(127,0,0,1)),
port: self.port.unwrap_or(SHIP_PORT),
}
}
}
pub struct ShipServerState<EG: EntityGateway> { pub struct ShipServerState<EG: EntityGateway> {
entity_gateway: EG, entity_gateway: EG,
@ -256,20 +310,13 @@ pub struct ShipServerState<EG: EntityGateway> {
pub rooms: Rooms, pub rooms: Rooms,
item_manager: items::ItemManager, item_manager: items::ItemManager,
quests: quests::QuestList, quests: quests::QuestList,
ip: Ipv4Addr,
port: u16,
} }
impl<EG: EntityGateway> ShipServerState<EG> { impl<EG: EntityGateway> ShipServerState<EG> {
pub fn new(entity_gateway: EG) -> ShipServerState<EG> {
ShipServerState {
entity_gateway: entity_gateway,
clients: HashMap::new(),
client_location: ClientLocation::new(),
level_table: CharacterLevelTable::new(),
name: "Sona-Nyl".into(),
rooms: [None; MAX_ROOMS],
item_manager: items::ItemManager::new(),
quests: quests::load_quests("data/quests.toml".into()).unwrap(),
}
pub fn builder() -> ShipServerStateBuilder<EG> {
ShipServerStateBuilder::new()
} }
async fn message(&mut self, id: ClientId, msg: &Message) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> { async fn message(&mut self, id: ClientId, msg: &Message) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
@ -493,3 +540,28 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
}).collect() }).collect()
} }
} }
#[async_trait::async_trait]
impl<EG: EntityGateway> InterserverActor for ShipServerState<EG> {
type SendMessage = ShipMessage;
type RecvMessage = LoginMessage;
type Error = ();
async fn on_connect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
vec![ /* ShipMessage::Authenticate(AuthToken("hi".into())), */ (id, ShipMessage::NewShip(Ship {
name: self.name.clone(),
ip: self.ip.clone(),
port: self.port,
block_count: 2,
})) ]
}
async fn action(&mut self, id: ServerId, msg: Self::RecvMessage) -> Result<Vec<(ServerId, Self::SendMessage)>, Self::Error> {
Ok(Vec::new())
}
async fn on_disconnect(&mut self, id: ServerId) -> Vec<(ServerId, Self::SendMessage)> {
Vec::new()
}
}

104
tests/test_bank.rs

@ -34,7 +34,9 @@ async fn test_bank_items_sent_in_character_login() {
} }
}).await; }).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
let packets = ship.handle(ClientId(1), &RecvShipPacket::MenuSelect(MenuSelect { let packets = ship.handle(ClientId(1), &RecvShipPacket::MenuSelect(MenuSelect {
@ -71,7 +73,9 @@ async fn test_request_bank_items() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -114,7 +118,9 @@ async fn test_request_stacked_bank_items() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -186,7 +192,9 @@ async fn test_request_bank_items_sorted() {
} }
}).await; }).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -249,7 +257,9 @@ async fn test_deposit_individual_item() {
} }
}).await; }).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
@ -316,7 +326,9 @@ async fn test_deposit_stacked_item() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
@ -383,7 +395,9 @@ async fn test_deposit_partial_stacked_item() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
@ -476,7 +490,9 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
@ -557,7 +573,9 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -649,7 +667,9 @@ async fn test_deposit_individual_item_in_full_bank() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -739,7 +759,9 @@ async fn test_deposit_stacked_item_in_full_bank() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -844,7 +866,9 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -899,7 +923,9 @@ async fn test_deposit_meseta() {
char1.meseta = 300; char1.meseta = 300;
entity_gateway.save_character(&char1).await; entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -935,7 +961,9 @@ async fn test_deposit_too_much_meseta() {
char1.bank_meseta = 999980; char1.bank_meseta = 999980;
entity_gateway.save_character(&char1).await; entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -972,7 +1000,9 @@ async fn test_deposit_meseta_when_bank_is_maxed() {
char1.bank_meseta = 999999; char1.bank_meseta = 999999;
entity_gateway.save_character(&char1).await; entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -1024,7 +1054,9 @@ async fn test_withdraw_individual_item() {
} }
}).await; }).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
@ -1089,7 +1121,9 @@ async fn test_withdraw_stacked_item() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
@ -1154,7 +1188,9 @@ async fn test_withdraw_partial_stacked_item() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
@ -1245,7 +1281,9 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
@ -1325,7 +1363,9 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -1417,7 +1457,9 @@ async fn test_withdraw_individual_item_in_full_inventory() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -1507,7 +1549,9 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -1613,7 +1657,9 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -1668,7 +1714,9 @@ async fn test_withdraw_meseta() {
char1.bank_meseta = 300; char1.bank_meseta = 300;
entity_gateway.save_character(&char1).await; entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -1704,7 +1752,9 @@ async fn test_withdraw_too_much_meseta() {
char1.bank_meseta = 300; char1.bank_meseta = 300;
entity_gateway.save_character(&char1).await; entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -1740,7 +1790,9 @@ async fn test_withdraw_meseta_inventory_is_maxed() {
char1.bank_meseta = 300; char1.bank_meseta = 300;
entity_gateway.save_character(&char1).await; entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;

16
tests/test_exp_gain.rs

@ -17,7 +17,9 @@ async fn test_character_gains_exp() {
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await; let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -54,7 +56,9 @@ async fn test_character_levels_up() {
char1.exp = 49; char1.exp = 49;
entity_gateway.save_character(&char1).await; entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -90,7 +94,9 @@ async fn test_character_levels_up_multiple_times() {
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await; let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -133,7 +139,9 @@ async fn test_one_character_gets_full_exp_and_other_attacker_gets_partial() {
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await; let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await; let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;

40
tests/test_item_pickup.rs

@ -49,7 +49,9 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
} }
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -116,7 +118,9 @@ async fn test_pick_up_item_stack_of_items_not_already_held() {
} }
}).await; }).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -185,7 +189,9 @@ async fn test_pick_up_meseta_when_inventory_full() {
char2.meseta = 300; char2.meseta = 300;
entity_gateway.save_character(&char2).await; entity_gateway.save_character(&char2).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -285,7 +291,9 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
} }
}).await; }).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -372,7 +380,9 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
} }
}).await; }).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -428,7 +438,9 @@ async fn test_can_not_drop_more_meseta_than_is_held() {
char1.meseta = 300; char1.meseta = 300;
entity_gateway.save_character(&char1).await; entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
@ -496,7 +508,9 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -545,7 +559,9 @@ async fn test_can_not_pick_up_meseta_when_full() {
char2.meseta = 300; char2.meseta = 300;
entity_gateway.save_character(&char2).await; entity_gateway.save_character(&char2).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -601,7 +617,9 @@ async fn test_meseta_caps_at_999999_when_trying_to_pick_up_more() {
char2.meseta = 300; char2.meseta = 300;
entity_gateway.save_character(&char2).await; entity_gateway.save_character(&char2).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -667,7 +685,9 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
}).await; }).await;
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await;

20
tests/test_item_use.rs

@ -36,7 +36,9 @@ async fn test_use_monomate() {
} }
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -90,7 +92,9 @@ async fn test_use_monomate_twice() {
} }
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -149,7 +153,9 @@ async fn test_use_last_monomate() {
} }
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -199,7 +205,9 @@ async fn test_use_nonstackable_tool() {
} }
}).await; }).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;
@ -238,7 +246,9 @@ async fn test_use_materials() {
} }
} }
let mut ship = ShipServerState::new(entity_gateway.clone());
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await; create_room(&mut ship, ClientId(1), "room", "").await;

Loading…
Cancel
Save