Merge pull request 'bunch of small fixes' (#127) from make_actually_work into master
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #127
This commit is contained in:
commit
f3bfa658cd
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -48,9 +48,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.57"
|
version = "1.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
]
|
]
|
||||||
@ -1040,6 +1040,7 @@ checksum = "739e9d7726dc32173fed2d69d17eef3c54682169e4e20ff1d0a45dcd37063cef"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "libpso"
|
name = "libpso"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
source = "git+http://git.sharnoth.com/jake/libpso#5051514fb1d3b39a7eb6ff97b624a9ceebd93e40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"psopacket",
|
"psopacket",
|
||||||
@ -1398,6 +1399,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "psopacket"
|
name = "psopacket"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
source = "git+http://git.sharnoth.com/jake/libpso#5051514fb1d3b39a7eb6ff97b624a9ceebd93e40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -32,4 +32,4 @@ refinery = { version = "0.5.0", features = ["postgres"] }
|
|||||||
sqlx = { version = "0.5.10", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] }
|
sqlx = { version = "0.5.10", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] }
|
||||||
strum = "0.19.5"
|
strum = "0.19.5"
|
||||||
strum_macros = "0.19"
|
strum_macros = "0.19"
|
||||||
anyhow = { version = "1.0.47", features = ["backtrace"] }
|
anyhow = { version = "1.0.68", features = ["backtrace"] }
|
||||||
|
@ -52,7 +52,7 @@ fn main() {
|
|||||||
|
|
||||||
for i in 0..5 {
|
for i in 0..5 {
|
||||||
let fake_user = NewUserAccountEntity {
|
let fake_user = NewUserAccountEntity {
|
||||||
email: format!("fake{}@email.com", i),
|
email: format!("fake{i}@email.com"),
|
||||||
username: if i == 0 { "hi".to_string() } else { format!("hi{}", i+1) },
|
username: if i == 0 { "hi".to_string() } else { format!("hi{}", i+1) },
|
||||||
password: bcrypt::hash("qwer", 5).unwrap(),
|
password: bcrypt::hash("qwer", 5).unwrap(),
|
||||||
guildcard: i + 1,
|
guildcard: i + 1,
|
||||||
@ -64,12 +64,12 @@ fn main() {
|
|||||||
};
|
};
|
||||||
let fake_user = entity_gateway.create_user(fake_user).await.unwrap();
|
let fake_user = entity_gateway.create_user(fake_user).await.unwrap();
|
||||||
entity_gateway.create_user_settings(NewUserSettingsEntity::new(fake_user.id)).await.unwrap();
|
entity_gateway.create_user_settings(NewUserSettingsEntity::new(fake_user.id)).await.unwrap();
|
||||||
let mut character = NewCharacterEntity::new(fake_user.id, 1);
|
let mut character = NewCharacterEntity::new(fake_user.id);
|
||||||
character.name = format!("Test Char {}", i*2);
|
character.name = format!("Test Char {}", i*2);
|
||||||
let character = entity_gateway.create_character(character).await.unwrap();
|
let character = entity_gateway.create_character(character).await.unwrap();
|
||||||
entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap();
|
entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap();
|
||||||
entity_gateway.set_bank_meseta(&character.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap();
|
entity_gateway.set_bank_meseta(&character.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap();
|
||||||
let mut character = NewCharacterEntity::new(fake_user.id, 1);
|
let mut character = NewCharacterEntity::new(fake_user.id);
|
||||||
character.slot = 2;
|
character.slot = 2;
|
||||||
character.name = "ItemRefactor".into();
|
character.name = "ItemRefactor".into();
|
||||||
character.exp = 80000000;
|
character.exp = 80000000;
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::io::Write;
|
||||||
use async_std::channel;
|
use async_std::channel;
|
||||||
use async_std::io::prelude::{ReadExt, WriteExt};
|
use async_std::io::prelude::{ReadExt, WriteExt};
|
||||||
use async_std::sync::{Arc, RwLock};
|
use async_std::sync::{Arc, RwLock};
|
||||||
use futures::future::Future;
|
use futures::future::Future;
|
||||||
use log::{trace, info, warn};
|
use log::{trace, info, warn, error};
|
||||||
|
|
||||||
use libpso::crypto::{PSOCipher, NullCipher, CipherError};
|
use libpso::crypto::{PSOCipher, NullCipher, CipherError};
|
||||||
use libpso::PacketParseError;
|
use libpso::PacketParseError;
|
||||||
@ -132,7 +133,7 @@ where
|
|||||||
match pkt_receiver.recv_pkts::<R>().await {
|
match pkt_receiver.recv_pkts::<R>().await {
|
||||||
Ok(pkts) => {
|
Ok(pkts) => {
|
||||||
for pkt in pkts {
|
for pkt in pkts {
|
||||||
info!("[recv from {:?}] {:#?}", client_id, pkt);
|
trace!("[recv from {:?}] {:#?}", client_id, pkt);
|
||||||
match state.handle(client_id, pkt).await {
|
match state.handle(client_id, pkt).await {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
for resp in response {
|
for resp in response {
|
||||||
@ -147,7 +148,27 @@ where
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("[client recv {:?}] error {:?} ", client_id, err);
|
error!("[client recv {:?}] error {:?} ", client_id, err);
|
||||||
|
|
||||||
|
let mut f = std::fs::File::options().create(true).append(true).open("errors.txt").unwrap();
|
||||||
|
f.write_all(format!("[{client_id:?}] {err:?}").as_bytes()).unwrap();
|
||||||
|
|
||||||
|
// disconnect client on an error
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,7 +194,7 @@ where
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
warn!("[client {:?} recv error] {:?}", client_id, err);
|
error!("[client {:?} recv error] {:?}", client_id, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,7 +227,7 @@ where
|
|||||||
Ok(pkt) => {
|
Ok(pkt) => {
|
||||||
info!("[send to {:?}] {:#?}", client_id, pkt);
|
info!("[send to {:?}] {:#?}", client_id, pkt);
|
||||||
if let Err(err) = send_pkt(&mut socket, &mut cipher, &pkt).await {
|
if let Err(err) = send_pkt(&mut socket, &mut cipher, &pkt).await {
|
||||||
warn!("error sending pkt {:#?} to {:?} {:?}", pkt, client_id, err);
|
error!("error sending pkt {:#?} to {:?} {:?}", pkt, client_id, err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -2,8 +2,8 @@ use std::convert::{From, Into};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use libpso::packet::ship::{UpdateConfig, WriteInfoboard, KeyboardConfig, GamepadConfig};
|
use libpso::packet::ship::{UpdateConfig, WriteInfoboard};
|
||||||
use libpso::character::settings::{DEFAULT_PALETTE_CONFIG, DEFAULT_TECH_MENU, DEFAULT_KEYBOARD_CONFIG1, DEFAULT_KEYBOARD_CONFIG2, DEFAULT_KEYBOARD_CONFIG3, DEFAULT_KEYBOARD_CONFIG4, DEFAULT_GAMEPAD_CONFIG};
|
use libpso::character::settings::{DEFAULT_PALETTE_CONFIG, DEFAULT_TECH_MENU};
|
||||||
use crate::entity::item::tech::Technique;
|
use crate::entity::item::tech::Technique;
|
||||||
use crate::entity::account::UserAccountId;
|
use crate::entity::account::UserAccountId;
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ pub struct CharacterAppearance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct TechLevel(pub u8);
|
pub struct TechLevel(pub u8);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
@ -167,16 +167,14 @@ pub struct CharacterTechniques {
|
|||||||
|
|
||||||
impl CharacterTechniques {
|
impl CharacterTechniques {
|
||||||
pub fn set_tech(&mut self, tech: Technique, level: TechLevel) {
|
pub fn set_tech(&mut self, tech: Technique, level: TechLevel) {
|
||||||
self.techs.insert(tech, TechLevel(level.0 - 1));
|
self.techs.insert(tech, TechLevel(level.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// from_bytes
|
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> [u8; 20] {
|
pub fn as_bytes(&self) -> [u8; 20] {
|
||||||
self.techs.iter()
|
self.techs.iter()
|
||||||
.fold([0xFF; 20], |mut techlist, (tech, level)| {
|
.fold([0xFF; 20], |mut techlist, (tech, level)| {
|
||||||
let index = tech.as_value();
|
let index = tech.as_value();
|
||||||
techlist[index as usize] = level.0;
|
techlist[index as usize] = level.0 - 1;
|
||||||
techlist
|
techlist
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -264,82 +262,6 @@ pub struct CharacterMaterials {
|
|||||||
pub tp: u32,
|
pub tp: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct CharacterKeyboardConfig {
|
|
||||||
pub keyboard_config: [u8; 0x16C],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CharacterKeyboardConfig {
|
|
||||||
fn default() -> CharacterKeyboardConfig {
|
|
||||||
CharacterKeyboardConfig {
|
|
||||||
keyboard_config: DEFAULT_KEYBOARD_CONFIG1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CharacterKeyboardConfig {
|
|
||||||
fn new(preset: usize) -> CharacterKeyboardConfig {
|
|
||||||
match preset {
|
|
||||||
1 => {
|
|
||||||
CharacterKeyboardConfig {
|
|
||||||
keyboard_config: DEFAULT_KEYBOARD_CONFIG1,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
2 => {
|
|
||||||
CharacterKeyboardConfig {
|
|
||||||
keyboard_config: DEFAULT_KEYBOARD_CONFIG2,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
3 => {
|
|
||||||
CharacterKeyboardConfig {
|
|
||||||
keyboard_config: DEFAULT_KEYBOARD_CONFIG3,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
4 => {
|
|
||||||
CharacterKeyboardConfig {
|
|
||||||
keyboard_config: DEFAULT_KEYBOARD_CONFIG4,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
CharacterKeyboardConfig {
|
|
||||||
keyboard_config: DEFAULT_KEYBOARD_CONFIG1,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, new_config: &KeyboardConfig) {
|
|
||||||
self.keyboard_config = new_config.keyboard_config;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> [u8; 0x16C] {
|
|
||||||
self.keyboard_config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct CharacterGamepadConfig {
|
|
||||||
pub gamepad_config: [u8; 0x38],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CharacterGamepadConfig {
|
|
||||||
fn default() -> CharacterGamepadConfig {
|
|
||||||
CharacterGamepadConfig {
|
|
||||||
gamepad_config: DEFAULT_GAMEPAD_CONFIG,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CharacterGamepadConfig {
|
|
||||||
pub fn update(&mut self, new_config: &GamepadConfig) {
|
|
||||||
self.gamepad_config = new_config.gamepad_config;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> [u8; 0x38] {
|
|
||||||
self.gamepad_config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default, derive_more::Display)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default, derive_more::Display)]
|
||||||
pub struct CharacterEntityId(pub u32);
|
pub struct CharacterEntityId(pub u32);
|
||||||
|
|
||||||
@ -363,12 +285,10 @@ pub struct NewCharacterEntity {
|
|||||||
|
|
||||||
pub tech_menu: CharacterTechMenu,
|
pub tech_menu: CharacterTechMenu,
|
||||||
pub option_flags: u32,
|
pub option_flags: u32,
|
||||||
pub keyboard_config: CharacterKeyboardConfig,
|
|
||||||
pub gamepad_config: CharacterGamepadConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NewCharacterEntity {
|
impl NewCharacterEntity {
|
||||||
pub fn new(user: UserAccountId, keyboard_config_preset: usize,) -> NewCharacterEntity {
|
pub fn new(user: UserAccountId) -> NewCharacterEntity {
|
||||||
NewCharacterEntity {
|
NewCharacterEntity {
|
||||||
user_id: user,
|
user_id: user,
|
||||||
slot: 0,
|
slot: 0,
|
||||||
@ -384,8 +304,6 @@ impl NewCharacterEntity {
|
|||||||
materials: CharacterMaterials::default(),
|
materials: CharacterMaterials::default(),
|
||||||
tech_menu: CharacterTechMenu::default(),
|
tech_menu: CharacterTechMenu::default(),
|
||||||
option_flags: 0,
|
option_flags: 0,
|
||||||
keyboard_config: CharacterKeyboardConfig::new(keyboard_config_preset),
|
|
||||||
gamepad_config: CharacterGamepadConfig::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,8 +329,6 @@ pub struct CharacterEntity {
|
|||||||
|
|
||||||
pub tech_menu: CharacterTechMenu,
|
pub tech_menu: CharacterTechMenu,
|
||||||
pub option_flags: u32,
|
pub option_flags: u32,
|
||||||
pub keyboard_config: CharacterKeyboardConfig,
|
|
||||||
pub gamepad_config: CharacterGamepadConfig,
|
|
||||||
|
|
||||||
pub playtime: u32,
|
pub playtime: u32,
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use std::convert::From;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
|
|
||||||
@ -10,9 +9,10 @@ use crate::entity::item::*;
|
|||||||
// TODO: better granularity?
|
// TODO: better granularity?
|
||||||
//#[derive(Error, Debug)]
|
//#[derive(Error, Debug)]
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("")]
|
|
||||||
pub enum GatewayError {
|
pub enum GatewayError {
|
||||||
|
#[error("unknown error")]
|
||||||
Error,
|
Error,
|
||||||
|
#[error("postgres error {0}")]
|
||||||
PgError(#[from] sqlx::Error)
|
PgError(#[from] sqlx::Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,12 +21,11 @@ pub enum GatewayError {
|
|||||||
pub trait EntityGateway: Send + Sync {
|
pub trait EntityGateway: Send + Sync {
|
||||||
type Transaction: EntityGatewayTransaction + Clone;
|
type Transaction: EntityGatewayTransaction + Clone;
|
||||||
|
|
||||||
async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, _func: F) -> Result<R, E>
|
async fn with_transaction<'a, F, Fut, R>(&'a mut self, _func: F) -> Result<R, anyhow::Error>
|
||||||
where
|
where
|
||||||
Fut: Future<Output = Result<(Self::Transaction, R), E>> + Send + 'a,
|
Fut: Future<Output = Result<(Self::Transaction, R), anyhow::Error>> + Send + 'a,
|
||||||
F: FnOnce(Self::Transaction) -> Fut + Send,
|
F: FnOnce(Self::Transaction) -> Fut + Send,
|
||||||
R: Send,
|
R: Send,
|
||||||
E: From<GatewayError>,
|
|
||||||
Self: Sized
|
Self: Sized
|
||||||
{
|
{
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
|
@ -72,6 +72,10 @@ impl EntityGateway for InMemoryGatewayTransaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> {
|
||||||
|
self.original_gateway.save_user_settings(settings).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> {
|
async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> {
|
||||||
copy_if_needed(&mut *self.working_gateway.characters.lock().await,
|
copy_if_needed(&mut *self.working_gateway.characters.lock().await,
|
||||||
&*self.original_gateway.characters.lock().await,
|
&*self.original_gateway.characters.lock().await,
|
||||||
@ -304,12 +308,11 @@ fn apply_modifiers(items: &BTreeMap<ItemEntityId, ItemEntity>,
|
|||||||
impl EntityGateway for InMemoryGateway {
|
impl EntityGateway for InMemoryGateway {
|
||||||
type Transaction = InMemoryGatewayTransaction;
|
type Transaction = InMemoryGatewayTransaction;
|
||||||
|
|
||||||
async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result<R, E>
|
async fn with_transaction<'a, F, Fut, R>(&'a mut self, func: F) -> Result<R, anyhow::Error>
|
||||||
where
|
where
|
||||||
Fut: Future<Output = Result<(Self::Transaction, R), E>> + Send + 'a,
|
Fut: Future<Output = Result<(Self::Transaction, R), anyhow::Error>> + Send + 'a,
|
||||||
F: FnOnce(Self::Transaction) -> Fut + Send,
|
F: FnOnce(Self::Transaction) -> Fut + Send,
|
||||||
R: Send,
|
R: Send,
|
||||||
E: From<GatewayError>,
|
|
||||||
{
|
{
|
||||||
let users = self.users.lock().await.clone();
|
let users = self.users.lock().await.clone();
|
||||||
let user_settings = self.user_settings.lock().await.clone();
|
let user_settings = self.user_settings.lock().await.clone();
|
||||||
@ -419,6 +422,12 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
.ok_or(GatewayError::Error)
|
.ok_or(GatewayError::Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> {
|
||||||
|
let mut user_settings = self.user_settings.lock().await;
|
||||||
|
user_settings.insert(settings.id, settings.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
|
async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
|
||||||
let characters = self.characters.lock().await;
|
let characters = self.characters.lock().await;
|
||||||
const NONE: Option<CharacterEntity> = None;
|
const NONE: Option<CharacterEntity> = None;
|
||||||
@ -453,8 +462,6 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
materials: character.materials,
|
materials: character.materials,
|
||||||
tech_menu: character.tech_menu,
|
tech_menu: character.tech_menu,
|
||||||
option_flags: character.option_flags,
|
option_flags: character.option_flags,
|
||||||
keyboard_config: character.keyboard_config,
|
|
||||||
gamepad_config: character.gamepad_config,
|
|
||||||
playtime: 0,
|
playtime: 0,
|
||||||
};
|
};
|
||||||
characters.insert(new_character.id, new_character.clone());
|
characters.insert(new_character.id, new_character.clone());
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
create table character_meseta (
|
create table character_meseta (
|
||||||
pchar integer references character (id) not null unique,
|
pchar integer references player_character (id) not null unique,
|
||||||
meseta integer not null,
|
meseta integer not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create table bank_meseta (
|
create table bank_meseta (
|
||||||
pchar integer references character (id) not null,
|
pchar integer references player_character (id) not null,
|
||||||
bank varchar(128) not null,
|
bank varchar(128) not null,
|
||||||
meseta integer not null,
|
meseta integer not null,
|
||||||
unique (pchar, bank)
|
unique (pchar, bank)
|
||||||
@ -12,4 +12,5 @@ create table bank_meseta (
|
|||||||
|
|
||||||
|
|
||||||
alter table player_character
|
alter table player_character
|
||||||
drop column meseta, bank_meseta;
|
drop column meseta,
|
||||||
|
drop column bank_meseta;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
create table trades (
|
create table trades (
|
||||||
id serial primary key not null,
|
id serial primary key not null,
|
||||||
character1 integer references character (id) not null,
|
character1 integer references player_character (id) not null,
|
||||||
character2 integer references character (id) not null,
|
character2 integer references player_character (id) not null
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
alter table player_character
|
||||||
|
add keyboard_config bytea not null,
|
||||||
|
add gamepad_config bytea not null;
|
@ -0,0 +1,5 @@
|
|||||||
|
alter table player_character
|
||||||
|
drop column playtime;
|
||||||
|
|
||||||
|
alter table player_character
|
||||||
|
add playtime integer not null;
|
@ -0,0 +1,3 @@
|
|||||||
|
alter table player_character
|
||||||
|
drop column keyboard_config,
|
||||||
|
drop column gamepad_config;
|
@ -0,0 +1,2 @@
|
|||||||
|
alter table player_character
|
||||||
|
add created_at timestamptz default current_timestamp not null;
|
@ -49,8 +49,8 @@ pub struct PgUserSettings {
|
|||||||
id: i32,
|
id: i32,
|
||||||
user_account: i32,
|
user_account: i32,
|
||||||
blocked_users: Vec<u8>, //[u32; 0x1E],
|
blocked_users: Vec<u8>, //[u32; 0x1E],
|
||||||
keyboard_config: Vec<u8>, //[u8; 0x16C],
|
key_config: Vec<u8>, //[u8; 0x16C],
|
||||||
gamepad_config: Vec<u8>, //[u8; 0x38],
|
joystick_config: Vec<u8>, //[u8; 0x38],
|
||||||
option_flags: i32,
|
option_flags: i32,
|
||||||
shortcuts: Vec<u8>, //[u8; 0xA40],
|
shortcuts: Vec<u8>, //[u8; 0xA40],
|
||||||
symbol_chats: Vec<u8>, //[u8; 0x4E0],
|
symbol_chats: Vec<u8>, //[u8; 0x4E0],
|
||||||
@ -64,8 +64,8 @@ impl From<PgUserSettings> for UserSettingsEntity {
|
|||||||
user_id: UserAccountId(other.user_account as u32),
|
user_id: UserAccountId(other.user_account as u32),
|
||||||
settings: settings::UserSettings {
|
settings: settings::UserSettings {
|
||||||
blocked_users: vec_to_array(other.blocked_users.chunks(4).map(|b| u32::from_le_bytes([b[0], b[1], b[2], b[3]])).collect()),
|
blocked_users: vec_to_array(other.blocked_users.chunks(4).map(|b| u32::from_le_bytes([b[0], b[1], b[2], b[3]])).collect()),
|
||||||
keyboard_config: vec_to_array(other.keyboard_config),
|
keyboard_config: vec_to_array(other.key_config),
|
||||||
gamepad_config: vec_to_array(other.gamepad_config),
|
gamepad_config: vec_to_array(other.joystick_config),
|
||||||
option_flags: other.option_flags as u32,
|
option_flags: other.option_flags as u32,
|
||||||
shortcuts: vec_to_array(other.shortcuts),
|
shortcuts: vec_to_array(other.shortcuts),
|
||||||
symbol_chats: vec_to_array(other.symbol_chats),
|
symbol_chats: vec_to_array(other.symbol_chats),
|
||||||
@ -217,8 +217,6 @@ pub struct PgCharacter {
|
|||||||
tp: i16,
|
tp: i16,
|
||||||
|
|
||||||
tech_menu: Vec<u8>,
|
tech_menu: Vec<u8>,
|
||||||
keyboard_config: Vec<u8>,
|
|
||||||
gamepad_config: Vec<u8>,
|
|
||||||
|
|
||||||
playtime: i32,
|
playtime: i32,
|
||||||
}
|
}
|
||||||
@ -246,7 +244,7 @@ impl From<PgCharacter> for CharacterEntity {
|
|||||||
prop_y: other.prop_y,
|
prop_y: other.prop_y,
|
||||||
},
|
},
|
||||||
techs: CharacterTechniques {
|
techs: CharacterTechniques {
|
||||||
techs: other.techs.iter().enumerate().take(19).filter(|(_, t)| **t != 0xFF).map(|(i, t)| (tech::Technique::from_value(i as u8), TechLevel(*t)) ).collect()
|
techs: other.techs.iter().enumerate().take(19).filter(|(_, t)| **t != 0xFF).map(|(i, t)| (tech::Technique::from_value(i as u8), TechLevel(*t + 1)) ).collect()
|
||||||
},
|
},
|
||||||
config: CharacterConfig {
|
config: CharacterConfig {
|
||||||
raw_data: vec_to_array(other.config)
|
raw_data: vec_to_array(other.config)
|
||||||
@ -270,12 +268,6 @@ impl From<PgCharacter> for CharacterEntity {
|
|||||||
tech_menu: CharacterTechMenu {
|
tech_menu: CharacterTechMenu {
|
||||||
tech_menu: vec_to_array(other.tech_menu)
|
tech_menu: vec_to_array(other.tech_menu)
|
||||||
},
|
},
|
||||||
keyboard_config: CharacterKeyboardConfig {
|
|
||||||
keyboard_config: vec_to_array(other.keyboard_config)
|
|
||||||
},
|
|
||||||
gamepad_config: CharacterGamepadConfig {
|
|
||||||
gamepad_config: vec_to_array(other.gamepad_config)
|
|
||||||
},
|
|
||||||
playtime: other.playtime as u32,
|
playtime: other.playtime as u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#![allow(clippy::explicit_auto_deref)]
|
#![allow(clippy::explicit_auto_deref)]
|
||||||
|
|
||||||
use std::convert::{From, TryFrom, Into};
|
use std::convert::{From, TryFrom, Into};
|
||||||
use futures::{Future, TryStreamExt};
|
use futures::Future;
|
||||||
use async_std::stream::StreamExt;
|
use async_std::stream::StreamExt;
|
||||||
use async_std::sync::{Arc, Mutex};
|
use async_std::sync::{Arc, Mutex};
|
||||||
use libpso::character::guildcard;
|
use libpso::character::guildcard;
|
||||||
@ -67,7 +67,7 @@ impl<'t> PostgresGateway<'t> {
|
|||||||
let pool = async_std::task::block_on(async move {
|
let pool = async_std::task::block_on(async move {
|
||||||
PgPoolOptions::new()
|
PgPoolOptions::new()
|
||||||
.max_connections(5)
|
.max_connections(5)
|
||||||
.connect(&format!("postgresql://{}:{}@{}:5432/{}", username, password, host, dbname)).await.unwrap()
|
.connect(&format!("postgresql://{username}:{password}@{host}:5432/{dbname}")).await.unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
PostgresGateway {
|
PostgresGateway {
|
||||||
@ -217,17 +217,26 @@ async fn save_user_settings(conn: &mut sqlx::PgConnection, settings: &UserSettin
|
|||||||
.bind(&settings.settings.symbol_chats.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.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?;
|
.execute(conn).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError>
|
async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError>
|
||||||
{
|
{
|
||||||
let q = r#"insert into player_character
|
let q = r#"insert into player_character
|
||||||
(user_account, slot, name, exp, class, section_id, costume, skin, face, head, hair, hair_r, hair_g, hair_b, prop_x, prop_y, techs,
|
(user_account, slot, name, exp, class,
|
||||||
config, infoboard, guildcard, power, mind, def, evade, luck, hp, tp, tech_menu, option_flags)
|
section_id, costume, skin, face, head,
|
||||||
|
hair, hair_r, hair_g, hair_b, prop_x,
|
||||||
|
prop_y, techs, config, infoboard, guildcard,
|
||||||
|
power, mind, def, evade, luck,
|
||||||
|
hp, tp, tech_menu, option_flags, playtime)
|
||||||
values
|
values
|
||||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31)
|
($1, $2, $3, $4, $5,
|
||||||
|
$6, $7, $8, $9, $10,
|
||||||
|
$11, $12, $13, $14, $15,
|
||||||
|
$16, $17, $18, $19, $20,
|
||||||
|
$21, $22, $23, $24, $25,
|
||||||
|
$26, $27, $28, $29, $30)
|
||||||
returning *;"#;
|
returning *;"#;
|
||||||
let character = sqlx::query_as::<_, PgCharacter>(q)
|
let character = sqlx::query_as::<_, PgCharacter>(q)
|
||||||
.bind(char.user_id.0)
|
.bind(char.user_id.0)
|
||||||
@ -259,6 +268,7 @@ async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntit
|
|||||||
.bind(char.materials.tp as i16)
|
.bind(char.materials.tp as i16)
|
||||||
.bind(char.tech_menu.tech_menu.to_vec())
|
.bind(char.tech_menu.tech_menu.to_vec())
|
||||||
.bind(char.option_flags as i32)
|
.bind(char.option_flags as i32)
|
||||||
|
.bind(0)
|
||||||
.fetch_one(conn).await?;
|
.fetch_one(conn).await?;
|
||||||
|
|
||||||
Ok(character.into())
|
Ok(character.into())
|
||||||
@ -266,17 +276,17 @@ async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntit
|
|||||||
|
|
||||||
async fn get_characters_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError>
|
async fn get_characters_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError>
|
||||||
{
|
{
|
||||||
let mut stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by slot")
|
let stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by created_at;")
|
||||||
.bind(user.id.0)
|
.bind(user.id.0)
|
||||||
.fetch(conn);
|
.fetch(conn);
|
||||||
const NONE: Option<CharacterEntity> = None;
|
|
||||||
let mut result = [NONE; 4];
|
|
||||||
while let Some(character) = stream.try_next().await? {
|
|
||||||
let index = character.slot as usize;
|
|
||||||
result[index] = Some(character.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
Ok(stream.fold(core::array::from_fn(|_| None), |mut acc, char| {
|
||||||
|
if let Ok(char) = char {
|
||||||
|
let slot = char.slot as usize;
|
||||||
|
acc[slot] = Some(char.into())
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
}).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -> Result<(), GatewayError>
|
async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -> Result<(), GatewayError>
|
||||||
@ -285,7 +295,7 @@ async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -
|
|||||||
user_account=$1, slot=$2, name=$3, exp=$4, class=$5, section_id=$6, costume=$7, skin=$8, face=$9, head=$10, hair=$11, hair_r=$12,
|
user_account=$1, slot=$2, name=$3, exp=$4, class=$5, section_id=$6, costume=$7, skin=$8, face=$9, head=$10, hair=$11, hair_r=$12,
|
||||||
hair_g=$13, hair_b=$14, prop_x=$15, prop_y=$16, techs=$17, config=$18, infoboard=$19, guildcard=$20, power=$21, mind=$22, def=$23,
|
hair_g=$13, hair_b=$14, prop_x=$15, prop_y=$16, techs=$17, config=$18, infoboard=$19, guildcard=$20, power=$21, mind=$22, def=$23,
|
||||||
evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29, playtime=$30
|
evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29, playtime=$30
|
||||||
where id=$31;"#;
|
where id=$31;"#;
|
||||||
sqlx::query(q)
|
sqlx::query(q)
|
||||||
.bind(char.user_id.0) // $1
|
.bind(char.user_id.0) // $1
|
||||||
.bind(char.slot as i16) // $2
|
.bind(char.slot as i16) // $2
|
||||||
@ -316,7 +326,7 @@ async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -
|
|||||||
.bind(char.materials.tp as i16) // $27
|
.bind(char.materials.tp as i16) // $27
|
||||||
.bind(char.tech_menu.tech_menu.to_vec()) // $28
|
.bind(char.tech_menu.tech_menu.to_vec()) // $28
|
||||||
.bind(char.option_flags as i32) // $29
|
.bind(char.option_flags as i32) // $29
|
||||||
.bind(char.playtime as i32) // $20
|
.bind(char.playtime as i32) // $30
|
||||||
.bind(char.id.0 as i32) // $31
|
.bind(char.id.0 as i32) // $31
|
||||||
.execute(conn).await?;
|
.execute(conn).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -530,7 +540,7 @@ async fn set_character_equips(conn: &mut sqlx::PgConnection, char_id: &Character
|
|||||||
|
|
||||||
async fn set_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError>
|
async fn set_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError>
|
||||||
{
|
{
|
||||||
sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set items = $2")
|
sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set meseta = $2")
|
||||||
.bind(char_id.0)
|
.bind(char_id.0)
|
||||||
.bind(meseta.0 as i32)
|
.bind(meseta.0 as i32)
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
@ -542,7 +552,7 @@ async fn get_character_meseta(conn: &mut sqlx::PgConnection, char_id: &Character
|
|||||||
{
|
{
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
struct PgMeseta(i32);
|
struct PgMeseta(i32);
|
||||||
let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1"#)
|
let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where pchar = $1"#)
|
||||||
.bind(char_id.0)
|
.bind(char_id.0)
|
||||||
.fetch_one(conn)
|
.fetch_one(conn)
|
||||||
.await?;
|
.await?;
|
||||||
@ -551,10 +561,10 @@ async fn get_character_meseta(conn: &mut sqlx::PgConnection, char_id: &Character
|
|||||||
|
|
||||||
async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError>
|
async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError>
|
||||||
{
|
{
|
||||||
sqlx::query("insert into bank_meseta values ($1, $2, $3) on conflict (pchar, bank) do update set items = $2")
|
sqlx::query("insert into bank_meseta values ($1, $2, $3) on conflict (pchar, bank) do update set meseta = $3")
|
||||||
.bind(char_id.0)
|
.bind(char_id.0)
|
||||||
.bind(meseta.0 as i32)
|
|
||||||
.bind(bank.0.clone())
|
.bind(bank.0.clone())
|
||||||
|
.bind(meseta.0 as i32)
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -564,7 +574,7 @@ async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit
|
|||||||
{
|
{
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
struct PgMeseta(i32);
|
struct PgMeseta(i32);
|
||||||
let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1 and bank = $2"#)
|
let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from bank_meseta where pchar = $1 and bank = $2"#)
|
||||||
.bind(char_id.0)
|
.bind(char_id.0)
|
||||||
.bind(bank.0.clone())
|
.bind(bank.0.clone())
|
||||||
.fetch_one(conn)
|
.fetch_one(conn)
|
||||||
@ -584,10 +594,10 @@ async fn create_trade(conn: &mut sqlx::PgConnection, char_id1: &CharacterEntityI
|
|||||||
|
|
||||||
async fn set_character_playtime(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError>
|
async fn set_character_playtime(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, playtime: u32) -> Result<(), GatewayError>
|
||||||
{
|
{
|
||||||
sqlx::query_as::<_, PgTradeEntity>(r#"update player_character set playtime=$2 where id=$1;"#)
|
sqlx::query(r#"update player_character set playtime=$2 where id=$1;"#)
|
||||||
.bind(char_id.0)
|
.bind(char_id.0)
|
||||||
.bind(playtime)
|
.bind(playtime)
|
||||||
.fetch_one(conn)
|
.execute(conn)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -596,18 +606,17 @@ async fn set_character_playtime(conn: &mut sqlx::PgConnection, char_id: &Charact
|
|||||||
impl<'t> EntityGateway for PostgresGateway<'t> {
|
impl<'t> EntityGateway for PostgresGateway<'t> {
|
||||||
type Transaction = PostgresTransaction<'t>;
|
type Transaction = PostgresTransaction<'t>;
|
||||||
|
|
||||||
async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result<R, E>
|
async fn with_transaction<'a, F, Fut, R>(&'a mut self, func: F) -> Result<R, anyhow::Error>
|
||||||
where
|
where
|
||||||
Fut: Future<Output = Result<(Self::Transaction, R), E>> + Send + 'a,
|
Fut: Future<Output = Result<(Self::Transaction, R), anyhow::Error>> + Send + 'a,
|
||||||
F: FnOnce(Self::Transaction) -> Fut + Send,
|
F: FnOnce(Self::Transaction) -> Fut + Send,
|
||||||
R: Send,
|
R: Send,
|
||||||
E: From<GatewayError>,
|
|
||||||
{
|
{
|
||||||
let transaction = PostgresTransaction {
|
let transaction = PostgresTransaction {
|
||||||
pgtransaction: Arc::new(Mutex::new(self.pool.begin().await.map_err(|_| ()).unwrap()))
|
pgtransaction: Arc::new(Mutex::new(self.pool.begin().await?))
|
||||||
};
|
};
|
||||||
let (transaction, result) = func(transaction).await.map_err(|_| ()).unwrap();
|
let (transaction, result) = func(transaction).await?;
|
||||||
transaction.commit().await.map_err(|_| ()).unwrap();
|
transaction.commit().await?;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,7 +521,7 @@ pub enum MagCellError {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum MagModifier {
|
pub enum MagModifier {
|
||||||
FeedMag{
|
FeedMag {
|
||||||
food: ItemEntityId,
|
food: ItemEntityId,
|
||||||
},
|
},
|
||||||
BankMag, // when putting a mag in the bank it truncates the values which has applications when raising degenerate mags
|
BankMag, // when putting a mag in the bank it truncates the values which has applications when raising degenerate mags
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
#![allow(clippy::type_complexity)]
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![feature(inline_const)]
|
#![feature(inline_const)]
|
||||||
#![feature(drain_filter)]
|
#![feature(drain_filter)]
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
#![feature(once_cell)]
|
#![feature(once_cell)]
|
||||||
#![feature(pin_macro)]
|
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
#![feature(error_generic_member_access)]
|
||||||
|
#![feature(provide_any)]
|
||||||
|
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
|
@ -38,13 +38,18 @@ pub const CHARACTER_PORT: u16 = 12001;
|
|||||||
pub const SHIP_MENU_ID: u32 = 1;
|
pub const SHIP_MENU_ID: u32 = 1;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
#[error("")]
|
|
||||||
pub enum CharacterError {
|
pub enum CharacterError {
|
||||||
|
#[error("invalid menu selection {0} {1}")]
|
||||||
InvalidMenuSelection(u32, u32),
|
InvalidMenuSelection(u32, u32),
|
||||||
|
#[error("client not found {0}")]
|
||||||
ClientNotFound(ClientId),
|
ClientNotFound(ClientId),
|
||||||
CouldNotLoadSettings,
|
#[error("could not load settings {0}")]
|
||||||
|
CouldNotLoadSettings(GatewayError),
|
||||||
|
#[error("could not load characters")]
|
||||||
CouldNotLoadCharacters,
|
CouldNotLoadCharacters,
|
||||||
|
#[error("could not load guildcard")]
|
||||||
CouldNotLoadGuildcard,
|
CouldNotLoadGuildcard,
|
||||||
|
#[error("gateway error {0}")]
|
||||||
GatewayError(#[from] GatewayError),
|
GatewayError(#[from] GatewayError),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +211,7 @@ async fn new_character<EG: EntityGateway + Clone>(entity_gateway: &mut EG, user:
|
|||||||
|
|
||||||
let character = entity_gateway.create_character(character).await?;
|
let character = entity_gateway.create_character(character).await?;
|
||||||
entity_gateway.set_character_meseta(&character.id, Meseta(300)).await?;
|
entity_gateway.set_character_meseta(&character.id, Meseta(300)).await?;
|
||||||
|
entity_gateway.set_bank_meseta(&character.id, &BankName("".into()), Meseta(0)).await?;
|
||||||
|
|
||||||
let new_weapon = match character.char_class {
|
let new_weapon = match character.char_class {
|
||||||
CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber,
|
CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber,
|
||||||
@ -260,6 +266,8 @@ async fn new_character<EG: EntityGateway + Clone>(entity_gateway: &mut EG, user:
|
|||||||
character_id: character.id,
|
character_id: character.id,
|
||||||
}).await?;
|
}).await?;
|
||||||
|
|
||||||
|
entity_gateway.change_mag_owner(&mag.id, &character).await?;
|
||||||
|
|
||||||
let mut monomates = Vec::new();
|
let mut monomates = Vec::new();
|
||||||
for _ in 0..4usize {
|
for _ in 0..4usize {
|
||||||
let monomate = entity_gateway.create_item(
|
let monomate = entity_gateway.create_item(
|
||||||
@ -385,7 +393,7 @@ impl<EG: EntityGateway + Clone> CharacterServerState<EG> {
|
|||||||
Ok(settings) => settings,
|
Ok(settings) => settings,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let user_settings = NewUserSettingsEntity::new(user.id);
|
let user_settings = NewUserSettingsEntity::new(user.id);
|
||||||
self.entity_gateway.create_user_settings(user_settings).await.map_err(|_| CharacterError::CouldNotLoadSettings)?
|
self.entity_gateway.create_user_settings(user_settings).await.map_err(CharacterError::CouldNotLoadSettings)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -741,7 +749,7 @@ impl<EG: EntityGateway + Clone> InterserverActor for CharacterServerState<EG> {
|
|||||||
|
|
||||||
|
|
||||||
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, 1); // it should not be possible for the client to specify the kbm config preset from the char create screen
|
let mut character = NewCharacterEntity::new(user.id);
|
||||||
character.slot = preview.slot;
|
character.slot = preview.slot;
|
||||||
character.name = String::from_utf16_lossy(&preview.character.name).trim_matches(char::from(0)).into();
|
character.name = String::from_utf16_lossy(&preview.character.name).trim_matches(char::from(0)).into();
|
||||||
character.section_id = preview.character.section_id.into();
|
character.section_id = preview.character.section_id.into();
|
||||||
|
@ -21,8 +21,8 @@ pub const LOGIN_PORT: u16 = 12000;
|
|||||||
pub const COMMUNICATION_PORT: u16 = 12123;
|
pub const COMMUNICATION_PORT: u16 = 12123;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
#[error("")]
|
|
||||||
pub enum LoginError {
|
pub enum LoginError {
|
||||||
|
#[error("dberror")]
|
||||||
DbError
|
DbError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,18 +395,18 @@ pub struct PatchConfig {
|
|||||||
|
|
||||||
pub fn load_config() -> PatchConfig {
|
pub fn load_config() -> PatchConfig {
|
||||||
let ini_file = match fs::File::open(std::path::Path::new("patch.ron")) {
|
let ini_file = match fs::File::open(std::path::Path::new("patch.ron")) {
|
||||||
Err(err) => panic!("Failed to open patch.ron config file. \n{}", err),
|
Err(err) => panic!("Failed to open patch.ron config file. \n{err}"),
|
||||||
Ok(ini_file) => ini_file,
|
Ok(ini_file) => ini_file,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
if let Err(err) = (&ini_file).read_to_string(&mut s) {
|
if let Err(err) = (&ini_file).read_to_string(&mut s) {
|
||||||
panic!("Failed to read patch.ron config file. \n{}", err);
|
panic!("Failed to read patch.ron config file. \n{err}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let config: PatchConfig = match from_str(s.as_str()) {
|
let config: PatchConfig = match from_str(s.as_str()) {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(err) => panic!("Failed to load values from patch.ron \n{}",err),
|
Err(err) => panic!("Failed to load values from patch.ron \n{err}"),
|
||||||
};
|
};
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ impl Clients {
|
|||||||
.into_inner())
|
.into_inner())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn with<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, ShipError>
|
pub async fn with<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
T: Send,
|
T: Send,
|
||||||
F: for<'b> FnOnce(&'b ClientState) -> BoxFuture<'b, T> + Send + 'a,
|
F: for<'b> FnOnce(&'b ClientState) -> BoxFuture<'b, T> + Send + 'a,
|
||||||
@ -53,7 +53,7 @@ impl Clients {
|
|||||||
Ok(func(&client).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>
|
pub async fn with_many<'a, T, F, const N: usize>(&'a self, client_ids: [ClientId; N], func: F) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
T: Send,
|
T: Send,
|
||||||
F: for<'b> FnOnce([RwLockReadGuard<'b, ClientState>; N]) -> BoxFuture<'b, T> + Send + 'a,
|
F: for<'b> FnOnce([RwLockReadGuard<'b, ClientState>; N]) -> BoxFuture<'b, T> + Send + 'a,
|
||||||
@ -85,7 +85,7 @@ impl Clients {
|
|||||||
Ok(func(client_states).await)
|
Ok(func(client_states).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn with_mut<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, ShipError>
|
pub async fn with_mut<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
T: Send,
|
T: Send,
|
||||||
F: for<'b> FnOnce(&'b mut ClientState) -> BoxFuture<'b, T> + Send + 'a,
|
F: for<'b> FnOnce(&'b mut ClientState) -> BoxFuture<'b, T> + Send + 'a,
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
|
|
||||||
mod drop_table;
|
mod drop_table;
|
||||||
mod rare_drop_table;
|
pub mod rare_drop_table;
|
||||||
mod generic_weapon;
|
mod generic_weapon;
|
||||||
mod generic_armor;
|
mod generic_armor;
|
||||||
mod generic_shield;
|
mod generic_shield;
|
||||||
@ -112,7 +112,7 @@ pub struct ItemDrop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct DropTable<R: Rng + SeedableRng> {
|
pub struct DropTable {
|
||||||
monster_stats: HashMap<MonsterType, MonsterDropStats>,
|
monster_stats: HashMap<MonsterType, MonsterDropStats>,
|
||||||
rare_table: RareDropTable,
|
rare_table: RareDropTable,
|
||||||
weapon_table: GenericWeaponTable,
|
weapon_table: GenericWeaponTable,
|
||||||
@ -121,11 +121,11 @@ pub struct DropTable<R: Rng + SeedableRng> {
|
|||||||
unit_table: GenericUnitTable,
|
unit_table: GenericUnitTable,
|
||||||
tool_table: ToolTable,
|
tool_table: ToolTable,
|
||||||
box_table: BoxDropTable,
|
box_table: BoxDropTable,
|
||||||
rng: R,
|
rng: rand_chacha::ChaCha20Rng,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Rng + SeedableRng> DropTable<R> {
|
impl DropTable {
|
||||||
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> DropTable<R> {
|
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> DropTable {
|
||||||
let monster_stats: HashMap<String, MonsterDropStats> = load_data_file(episode, difficulty, section_id, "monster_dar.toml");
|
let monster_stats: HashMap<String, MonsterDropStats> = load_data_file(episode, difficulty, section_id, "monster_dar.toml");
|
||||||
|
|
||||||
DropTable {
|
DropTable {
|
||||||
@ -137,7 +137,21 @@ impl<R: Rng + SeedableRng> DropTable<R> {
|
|||||||
unit_table: GenericUnitTable::new(episode, difficulty, section_id),
|
unit_table: GenericUnitTable::new(episode, difficulty, section_id),
|
||||||
tool_table: ToolTable::new(episode, difficulty, section_id),
|
tool_table: ToolTable::new(episode, difficulty, section_id),
|
||||||
box_table: BoxDropTable::new(episode, difficulty, section_id),
|
box_table: BoxDropTable::new(episode, difficulty, section_id),
|
||||||
rng: R::from_entropy(),
|
rng: rand_chacha::ChaCha20Rng::from_entropy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn builder() -> DropTableBuilder {
|
||||||
|
DropTableBuilder {
|
||||||
|
monster_stats: None,
|
||||||
|
rare_table: None,
|
||||||
|
weapon_table: None,
|
||||||
|
armor_table: None,
|
||||||
|
shield_table: None,
|
||||||
|
unit_table: None,
|
||||||
|
tool_table: None,
|
||||||
|
box_table: None,
|
||||||
|
rng: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,6 +203,66 @@ impl<R: Rng + SeedableRng> DropTable<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct DropTableBuilder {
|
||||||
|
monster_stats: Option<HashMap<MonsterType, MonsterDropStats>>,
|
||||||
|
rare_table: Option<RareDropTable>,
|
||||||
|
weapon_table: Option<GenericWeaponTable>,
|
||||||
|
armor_table: Option<GenericArmorTable>,
|
||||||
|
shield_table: Option<GenericShieldTable>,
|
||||||
|
unit_table: Option<GenericUnitTable>,
|
||||||
|
tool_table: Option<ToolTable>,
|
||||||
|
box_table: Option<BoxDropTable>,
|
||||||
|
rng: Option<rand_chacha::ChaCha20Rng>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add the rest of these later I just need these ones right now
|
||||||
|
impl DropTableBuilder {
|
||||||
|
#[must_use]
|
||||||
|
pub fn monster_stats(mut self, monster_stats: HashMap<MonsterType, MonsterDropStats>) -> DropTableBuilder {
|
||||||
|
self.monster_stats = Some(monster_stats);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn monster_stat(mut self, monster_type: MonsterType, drop_stats: MonsterDropStats) -> DropTableBuilder {
|
||||||
|
match &mut self.monster_stats {
|
||||||
|
Some(monster_stats) => {
|
||||||
|
monster_stats.insert(monster_type, drop_stats);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let mut monster_stats = HashMap::default();
|
||||||
|
monster_stats.insert(monster_type, drop_stats);
|
||||||
|
self.monster_stats = Some(monster_stats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn rare_table(mut self, rare_table: RareDropTable) -> DropTableBuilder {
|
||||||
|
self.rare_table = Some(rare_table);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self, episode: Episode, difficulty: Difficulty, section_id: SectionID) -> DropTable {
|
||||||
|
DropTable {
|
||||||
|
monster_stats: self.monster_stats.unwrap_or_else(|| {
|
||||||
|
let monster_stats: HashMap<String, MonsterDropStats> = load_data_file(episode, difficulty, section_id, "monster_dar.toml");
|
||||||
|
monster_stats.into_iter().map(|(m, s)| (m.parse().unwrap(), s)).collect()
|
||||||
|
}),
|
||||||
|
rare_table: self.rare_table.unwrap_or_else(|| RareDropTable::new(episode, difficulty, section_id)),
|
||||||
|
weapon_table: self.weapon_table.unwrap_or_else(|| GenericWeaponTable::new(episode, difficulty, section_id)),
|
||||||
|
armor_table: self.armor_table.unwrap_or_else(|| GenericArmorTable::new(episode, difficulty, section_id)),
|
||||||
|
shield_table: self.shield_table.unwrap_or_else(|| GenericShieldTable::new(episode, difficulty, section_id)),
|
||||||
|
unit_table: self.unit_table.unwrap_or_else(|| GenericUnitTable::new(episode, difficulty, section_id)),
|
||||||
|
tool_table: self.tool_table.unwrap_or_else(|| ToolTable::new(episode, difficulty, section_id)),
|
||||||
|
box_table: self.box_table.unwrap_or_else(|| BoxDropTable::new(episode, difficulty, section_id)),
|
||||||
|
rng: self.rng.unwrap_or_else(rand_chacha::ChaCha20Rng::from_entropy),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -203,6 +277,6 @@ mod test {
|
|||||||
let section_id = vec![SectionID::Viridia, SectionID::Greenill, SectionID::Skyly, SectionID::Bluefull, SectionID::Purplenum,
|
let section_id = vec![SectionID::Viridia, SectionID::Greenill, SectionID::Skyly, SectionID::Bluefull, SectionID::Purplenum,
|
||||||
SectionID::Pinkal, SectionID::Redria, SectionID::Oran, SectionID::Yellowboze, SectionID::Whitill]
|
SectionID::Pinkal, SectionID::Redria, SectionID::Oran, SectionID::Yellowboze, SectionID::Whitill]
|
||||||
.into_iter().choose(&mut rng).unwrap();
|
.into_iter().choose(&mut rng).unwrap();
|
||||||
DropTable::<rand_chacha::ChaCha20Rng>::new(episode, difficulty, section_id);
|
DropTable::new(episode, difficulty, section_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,9 +50,9 @@ impl RareDropItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct RareDropRate {
|
pub struct RareDropRate {
|
||||||
rate: f32,
|
pub rate: f32,
|
||||||
item: RareDropItem
|
pub item: RareDropItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -71,30 +71,41 @@ pub struct RareDropTable {
|
|||||||
shield_stats: GenericShieldTable,
|
shield_stats: GenericShieldTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_default_monster_rates(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> HashMap<MonsterType, Vec<RareDropRate>> {
|
||||||
|
let cfg: HashMap<String, Vec<RareDropConfigEntity>> = load_data_file(episode, difficulty, section_id, "rare_rate.toml");
|
||||||
|
|
||||||
|
cfg.into_iter()
|
||||||
|
.map(|(monster, drops)| {
|
||||||
|
let monster = monster.parse().unwrap();
|
||||||
|
let drops = drops.into_iter().map(|drop| {
|
||||||
|
RareDropRate {
|
||||||
|
rate: drop.rate,
|
||||||
|
item: RareDropItem::from_string(drop.item),
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
(monster, drops)
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
impl RareDropTable {
|
impl RareDropTable {
|
||||||
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable {
|
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable {
|
||||||
let cfg: HashMap<String, Vec<RareDropConfigEntity>> = load_data_file(episode, difficulty, section_id, "rare_rate.toml");
|
|
||||||
|
|
||||||
let rates = cfg.into_iter()
|
|
||||||
.map(|(monster, drops)| {
|
|
||||||
let monster = monster.parse().unwrap();
|
|
||||||
let drops = drops.into_iter().map(|drop| {
|
|
||||||
RareDropRate {
|
|
||||||
rate: drop.rate,
|
|
||||||
item: RareDropItem::from_string(drop.item),
|
|
||||||
}
|
|
||||||
}).collect();
|
|
||||||
(monster, drops)
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
RareDropTable {
|
RareDropTable {
|
||||||
rates,
|
rates: load_default_monster_rates(episode, difficulty, section_id),
|
||||||
attribute_table: AttributeTable::new(episode, difficulty, section_id),
|
attribute_table: AttributeTable::new(episode, difficulty, section_id),
|
||||||
armor_stats: GenericArmorTable::new(episode, difficulty, section_id),
|
armor_stats: GenericArmorTable::new(episode, difficulty, section_id),
|
||||||
shield_stats: GenericShieldTable::new(episode, difficulty, section_id),
|
shield_stats: GenericShieldTable::new(episode, difficulty, section_id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn builder() -> RareDropTableBuilder {
|
||||||
|
RareDropTableBuilder {
|
||||||
|
rates: None,
|
||||||
|
attribute_table: None,
|
||||||
|
armor_stats: None,
|
||||||
|
shield_stats: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apply_item_stats<R: Rng>(&self, map_area: &MapArea, item: RareDropItem, rng: &mut R) -> ItemDropType {
|
pub fn apply_item_stats<R: Rng>(&self, map_area: &MapArea, item: RareDropItem, rng: &mut R) -> ItemDropType {
|
||||||
match item {
|
match item {
|
||||||
RareDropItem::Weapon(weapon) => {
|
RareDropItem::Weapon(weapon) => {
|
||||||
@ -155,3 +166,46 @@ impl RareDropTable {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct RareDropTableBuilder {
|
||||||
|
rates: Option<HashMap<MonsterType, Vec<RareDropRate>>>,
|
||||||
|
attribute_table: Option<AttributeTable>,
|
||||||
|
armor_stats: Option<GenericArmorTable>,
|
||||||
|
shield_stats: Option<GenericShieldTable>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: add the rest of these later I just need these ones right now
|
||||||
|
impl RareDropTableBuilder {
|
||||||
|
pub fn rates(mut self, rates: HashMap<MonsterType, Vec<RareDropRate>>) -> RareDropTableBuilder {
|
||||||
|
self.rates = Some(rates);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn rate(mut self, monster_type: MonsterType, drop_rate: RareDropRate) -> RareDropTableBuilder {
|
||||||
|
match &mut self.rates {
|
||||||
|
Some(rates) => {
|
||||||
|
rates.entry(monster_type)
|
||||||
|
.or_insert(Vec::new())
|
||||||
|
.push(drop_rate);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let mut rates = HashMap::default();
|
||||||
|
rates.insert(monster_type, vec![drop_rate]);
|
||||||
|
self.rates = Some(rates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self, episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable {
|
||||||
|
RareDropTable {
|
||||||
|
rates: self.rates.unwrap_or_else(|| load_default_monster_rates(episode, difficulty, section_id)),
|
||||||
|
attribute_table: self.attribute_table.unwrap_or_else(|| AttributeTable::new(episode, difficulty, section_id)),
|
||||||
|
armor_stats: self.armor_stats.unwrap_or_else(|| GenericArmorTable::new(episode, difficulty, section_id)),
|
||||||
|
shield_stats: self.shield_stats.unwrap_or_else(|| GenericShieldTable::new(episode, difficulty, section_id)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -35,7 +35,7 @@ pub(super) fn take_item_from_floor<EG, TR>(
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
item_id: ClientItemId
|
item_id: ClientItemId
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Send,
|
EG: EntityGateway + Send,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -54,7 +54,7 @@ where
|
|||||||
pub(super) fn add_floor_item_to_inventory<EG, TR>(
|
pub(super) fn add_floor_item_to_inventory<EG, TR>(
|
||||||
character: &CharacterEntity
|
character: &CharacterEntity
|
||||||
) -> impl Fn((ItemStateProxy, TR), FloorItem)
|
) -> impl Fn((ItemStateProxy, TR), FloorItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), TriggerCreateItem), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), TriggerCreateItem), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + Clone + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + Clone + 'static,
|
||||||
@ -103,7 +103,7 @@ pub(super) fn take_item_from_inventory<EG, TR>(
|
|||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -111,7 +111,7 @@ where
|
|||||||
move |(mut item_state, mut transaction), _| {
|
move |(mut item_state, mut transaction), _| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut inventory = item_state.inventory(&character_id).await?;
|
let mut inventory = item_state.inventory(&character_id).await?;
|
||||||
let item = inventory.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoFloorItem(item_id))?;
|
let item = inventory.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoInventoryItem(item_id))?;
|
||||||
|
|
||||||
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
|
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
|
||||||
item_state.set_inventory(inventory).await;
|
item_state.set_inventory(inventory).await;
|
||||||
@ -127,7 +127,7 @@ pub(super) fn add_inventory_item_to_shared_floor<EG, TR>(
|
|||||||
map_area: MapArea,
|
map_area: MapArea,
|
||||||
drop_position: (f32, f32, f32),
|
drop_position: (f32, f32, f32),
|
||||||
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -160,7 +160,7 @@ pub(super) fn take_meseta_from_inventory<EG, TR>(
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), ()), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -181,7 +181,7 @@ pub(super) fn add_meseta_to_inventory<EG, TR>(
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
amount: u32
|
amount: u32
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), ()), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -204,7 +204,7 @@ pub(super) fn add_meseta_to_shared_floor<EG, TR>(
|
|||||||
map_area: MapArea,
|
map_area: MapArea,
|
||||||
drop_position: (f32, f32)
|
drop_position: (f32, f32)
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -233,7 +233,7 @@ pub(super) fn take_meseta_from_bank<EG, TR>(
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), ()), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -243,6 +243,7 @@ where
|
|||||||
let mut bank = item_state.bank(&character_id).await?;
|
let mut bank = item_state.bank(&character_id).await?;
|
||||||
bank.remove_meseta(amount)?;
|
bank.remove_meseta(amount)?;
|
||||||
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
|
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
|
||||||
|
item_state.set_bank(bank).await;
|
||||||
|
|
||||||
Ok(((item_state, transaction), ()))
|
Ok(((item_state, transaction), ()))
|
||||||
})
|
})
|
||||||
@ -253,7 +254,7 @@ pub(super) fn add_meseta_from_bank_to_inventory<EG, TR>(
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), ()), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -274,7 +275,7 @@ pub(super) fn add_meseta_to_bank<EG, TR>(
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), ()), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -284,6 +285,7 @@ where
|
|||||||
let mut bank = item_state.bank(&character_id).await?;
|
let mut bank = item_state.bank(&character_id).await?;
|
||||||
bank.add_meseta(amount)?;
|
bank.add_meseta(amount)?;
|
||||||
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
|
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
|
||||||
|
item_state.set_bank(bank).await;
|
||||||
|
|
||||||
Ok(((item_state, transaction), ()))
|
Ok(((item_state, transaction), ()))
|
||||||
})
|
})
|
||||||
@ -296,7 +298,7 @@ pub(super) fn take_item_from_bank<EG, TR>(
|
|||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), BankItem), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), BankItem), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -316,7 +318,7 @@ where
|
|||||||
pub(super) fn add_bank_item_to_inventory<EG, TR>(
|
pub(super) fn add_bank_item_to_inventory<EG, TR>(
|
||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
) -> impl Fn((ItemStateProxy, TR), BankItem)
|
) -> impl Fn((ItemStateProxy, TR), BankItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -367,7 +369,7 @@ where
|
|||||||
pub(super) fn add_inventory_item_to_bank<EG, TR>(
|
pub(super) fn add_inventory_item_to_bank<EG, TR>(
|
||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), ()), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -403,7 +405,7 @@ pub(super) fn equip_inventory_item<EG, TR>(
|
|||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
equip_slot: u8,
|
equip_slot: u8,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), ()), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -425,7 +427,7 @@ pub(super) fn unequip_inventory_item<EG, TR>(
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), ()), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -448,7 +450,7 @@ pub(super) fn sort_inventory_items<EG, TR>(
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
item_ids: Vec<ClientItemId>,
|
item_ids: Vec<ClientItemId>,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), ()), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), ()), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -470,7 +472,7 @@ where
|
|||||||
pub(super) fn use_consumed_item<EG, TR>(
|
pub(super) fn use_consumed_item<EG, TR>(
|
||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<ApplyItemAction>), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<ApplyItemAction>), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -497,7 +499,7 @@ pub(super) fn feed_mag_item<EG, TR>(
|
|||||||
character: CharacterEntity,
|
character: CharacterEntity,
|
||||||
mag_item_id: ClientItemId,
|
mag_item_id: ClientItemId,
|
||||||
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), CharacterEntity), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), CharacterEntity), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -551,7 +553,7 @@ pub(super) fn add_bought_item_to_inventory<'a, EG, TR>(
|
|||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>> + Send + 'a>>
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>> + Send + 'a>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -613,7 +615,7 @@ where
|
|||||||
pub(super) fn sell_inventory_item<EG, TR>(
|
pub(super) fn sell_inventory_item<EG, TR>(
|
||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -643,7 +645,7 @@ async fn iterate_inner<'a, EG, TR, I, O, T, F, FR>(
|
|||||||
mut input: Vec<I>,
|
mut input: Vec<I>,
|
||||||
func: F,
|
func: F,
|
||||||
arg: T,
|
arg: T,
|
||||||
) -> Result<((ItemStateProxy, TR), Vec<O>), ItemStateError>
|
) -> Result<((ItemStateProxy, TR), Vec<O>), anyhow::Error>
|
||||||
where
|
where
|
||||||
'a: 'async_recursion,
|
'a: 'async_recursion,
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
@ -653,7 +655,7 @@ where
|
|||||||
T: Clone + Send + Sync,
|
T: Clone + Send + Sync,
|
||||||
F: Fn(I) -> FR + Send + Sync + Clone + 'static,
|
F: Fn(I) -> FR + Send + Sync + Clone + 'static,
|
||||||
FR: Fn((ItemStateProxy, TR), T)
|
FR: Fn((ItemStateProxy, TR), T)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), O), ItemStateError>> + Send + Sync,
|
-> BoxFuture<Result<((ItemStateProxy, TR), O), anyhow::Error>> + Send + Sync,
|
||||||
{
|
{
|
||||||
let item = match input.pop() {
|
let item = match input.pop() {
|
||||||
Some(item) => item,
|
Some(item) => item,
|
||||||
@ -673,7 +675,7 @@ pub(super) fn iterate<EG, TR, I, O, T, F, FR>(
|
|||||||
input: Vec<I>,
|
input: Vec<I>,
|
||||||
func: F,
|
func: F,
|
||||||
) -> impl Fn((ItemStateProxy, TR), T)
|
) -> impl Fn((ItemStateProxy, TR), T)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<O>), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<O>), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -682,7 +684,7 @@ where
|
|||||||
T: Send + Clone + 'static + std::fmt::Debug,
|
T: Send + Clone + 'static + std::fmt::Debug,
|
||||||
F: Fn(I) -> FR + Send + Sync + Clone + 'static,
|
F: Fn(I) -> FR + Send + Sync + Clone + 'static,
|
||||||
FR: Fn((ItemStateProxy, TR), T)
|
FR: Fn((ItemStateProxy, TR), T)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), O), ItemStateError>> + Send + Sync,
|
-> BoxFuture<Result<((ItemStateProxy, TR), O), anyhow::Error>> + Send + Sync,
|
||||||
T: Clone + Send + Sync,
|
T: Clone + Send + Sync,
|
||||||
{
|
{
|
||||||
move |(item_state, transaction), arg| {
|
move |(item_state, transaction), arg| {
|
||||||
@ -701,7 +703,7 @@ async fn foreach_inner<'a, EG, TR, O, T, F, I>(
|
|||||||
state: (ItemStateProxy, TR),
|
state: (ItemStateProxy, TR),
|
||||||
mut input: I,
|
mut input: I,
|
||||||
func: Arc<F>,
|
func: Arc<F>,
|
||||||
) -> Result<((ItemStateProxy, TR), Vec<O>), ItemStateError>
|
) -> Result<((ItemStateProxy, TR), Vec<O>), anyhow::Error>
|
||||||
where
|
where
|
||||||
'a: 'async_recursion,
|
'a: 'async_recursion,
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
@ -709,7 +711,7 @@ where
|
|||||||
O: Send,
|
O: Send,
|
||||||
T: Send,
|
T: Send,
|
||||||
F: Fn((ItemStateProxy, TR), T)
|
F: Fn((ItemStateProxy, TR), T)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), O), ItemStateError>> + Send + Sync,
|
-> BoxFuture<Result<((ItemStateProxy, TR), O), anyhow::Error>> + Send + Sync,
|
||||||
I: Iterator<Item = T> + Send + Sync + 'static,
|
I: Iterator<Item = T> + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
let item = match input.next() {
|
let item = match input.next() {
|
||||||
@ -728,14 +730,14 @@ where
|
|||||||
pub(super) fn foreach<EG, TR, O, T, F, I>(
|
pub(super) fn foreach<EG, TR, O, T, F, I>(
|
||||||
func: F
|
func: F
|
||||||
) -> impl Fn((ItemStateProxy, TR), I)
|
) -> impl Fn((ItemStateProxy, TR), I)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<O>), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<O>), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
O: Send,
|
O: Send,
|
||||||
T: Send + Clone + 'static + std::fmt::Debug,
|
T: Send + Clone + 'static + std::fmt::Debug,
|
||||||
F: Fn((ItemStateProxy, TR), T)
|
F: Fn((ItemStateProxy, TR), T)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), O), ItemStateError>> + Send + Sync + 'static,
|
-> BoxFuture<Result<((ItemStateProxy, TR), O), anyhow::Error>> + Send + Sync + 'static,
|
||||||
T: Send + Sync,
|
T: Send + Sync,
|
||||||
I: IntoIterator<Item = T> + Send + Sync + 'static,
|
I: IntoIterator<Item = T> + Send + Sync + 'static,
|
||||||
I::IntoIter: Send + Sync,
|
I::IntoIter: Send + Sync,
|
||||||
@ -754,7 +756,7 @@ where
|
|||||||
pub(super) fn insert<'a, EG, TR, T>(
|
pub(super) fn insert<'a, EG, TR, T>(
|
||||||
element: T
|
element: T
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy, TR), T), ItemStateError>> + Send + 'a>>
|
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy, TR), T), anyhow::Error>> + Send + 'a>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -772,12 +774,12 @@ pub(super) fn fork<EG, TR, F1, F2, T, O1, O2>(
|
|||||||
func1: F1,
|
func1: F1,
|
||||||
func2: F2,
|
func2: F2,
|
||||||
) -> impl Fn((ItemStateProxy, TR), T)
|
) -> impl Fn((ItemStateProxy, TR), T)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), (O1, O2)), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), (O1, O2)), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
F1: Fn((ItemStateProxy, TR), T) -> BoxFuture<Result<((ItemStateProxy, TR), O1), ItemStateError>> + Send + Sync + 'static,
|
F1: Fn((ItemStateProxy, TR), T) -> BoxFuture<Result<((ItemStateProxy, TR), O1), anyhow::Error>> + Send + Sync + 'static,
|
||||||
F2: Fn((ItemStateProxy, TR), T) -> BoxFuture<Result<((ItemStateProxy, TR), O2), ItemStateError>> + Send + Sync + 'static,
|
F2: Fn((ItemStateProxy, TR), T) -> BoxFuture<Result<((ItemStateProxy, TR), O2), anyhow::Error>> + Send + Sync + 'static,
|
||||||
T: Send + Sync + Clone + 'static,
|
T: Send + Sync + Clone + 'static,
|
||||||
O1: Send,
|
O1: Send,
|
||||||
O2: Send,
|
O2: Send,
|
||||||
@ -799,7 +801,7 @@ where
|
|||||||
pub(super) fn add_item_to_inventory<EG, TR>(
|
pub(super) fn add_item_to_inventory<EG, TR>(
|
||||||
character: CharacterEntity,
|
character: CharacterEntity,
|
||||||
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>> + Clone
|
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>> + Clone
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -829,7 +831,7 @@ pub(super) fn record_trade<EG, TR>(
|
|||||||
character_to: CharacterEntityId,
|
character_to: CharacterEntityId,
|
||||||
character_from: CharacterEntityId,
|
character_from: CharacterEntityId,
|
||||||
) -> impl Fn((ItemStateProxy, TR), Vec<InventoryItem>)
|
) -> impl Fn((ItemStateProxy, TR), Vec<InventoryItem>)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<InventoryItem>), ItemStateError>> + Clone
|
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<InventoryItem>), anyhow::Error>> + Clone
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -855,7 +857,7 @@ where
|
|||||||
|
|
||||||
pub(super) fn assign_new_item_id<EG, TR>(
|
pub(super) fn assign_new_item_id<EG, TR>(
|
||||||
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>> + Clone
|
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>> + Clone
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -873,7 +875,7 @@ pub(super) fn convert_item_drop_to_floor_item<EG, TR>(
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
item_drop: ItemDrop,
|
item_drop: ItemDrop,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ())
|
) -> impl Fn((ItemStateProxy, TR), ())
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), ItemStateError>> + Clone
|
-> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>> + Clone
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -974,7 +976,7 @@ where
|
|||||||
pub(super) fn add_item_to_local_floor<EG, TR>(
|
pub(super) fn add_item_to_local_floor<EG, TR>(
|
||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
) -> impl Fn((ItemStateProxy, TR), FloorItem)
|
) -> impl Fn((ItemStateProxy, TR), FloorItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), FloorItem), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -993,7 +995,7 @@ where
|
|||||||
pub(super) fn apply_modifier_to_inventory_item<EG, TR>(
|
pub(super) fn apply_modifier_to_inventory_item<EG, TR>(
|
||||||
modifier: ItemModifier,
|
modifier: ItemModifier,
|
||||||
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), InventoryItem), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -1006,7 +1008,7 @@ where
|
|||||||
weapon.apply_modifier(&modifier);
|
weapon.apply_modifier(&modifier);
|
||||||
transaction.gateway().add_weapon_modifier(entity_id, modifier).await?;
|
transaction.gateway().add_weapon_modifier(entity_id, modifier).await?;
|
||||||
},
|
},
|
||||||
_ => return Err(ItemStateError::InvalidModifier)
|
_ => return Err(ItemStateError::InvalidModifier.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(((item_state, transaction), inventory_item))
|
Ok(((item_state, transaction), inventory_item))
|
||||||
@ -1016,7 +1018,7 @@ where
|
|||||||
|
|
||||||
pub(super) fn as_individual_item<EG, TR>(
|
pub(super) fn as_individual_item<EG, TR>(
|
||||||
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), IndividualItemDetail), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), IndividualItemDetail), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -1025,7 +1027,7 @@ where
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let item = match inventory_item.item {
|
let item = match inventory_item.item {
|
||||||
InventoryItemDetail::Individual(individual_item) => individual_item,
|
InventoryItemDetail::Individual(individual_item) => individual_item,
|
||||||
_ => return Err(ItemStateError::WrongItemType(inventory_item.item_id))
|
_ => return Err(ItemStateError::WrongItemType(inventory_item.item_id).into())
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(((item_state, transaction), item))
|
Ok(((item_state, transaction), item))
|
||||||
@ -1038,7 +1040,7 @@ pub(super) fn apply_item_action_packets<EG, TR>(
|
|||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
area_client: AreaClient,
|
area_client: AreaClient,
|
||||||
) -> impl Fn((ItemStateProxy, TR), ApplyItemAction)
|
) -> impl Fn((ItemStateProxy, TR), ApplyItemAction)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<SendShipPacket>), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<SendShipPacket>), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
@ -1095,7 +1097,7 @@ where
|
|||||||
pub(super) fn apply_item_action_character<EG, TR>(
|
pub(super) fn apply_item_action_character<EG, TR>(
|
||||||
character: &CharacterEntity
|
character: &CharacterEntity
|
||||||
) -> impl Fn((ItemStateProxy, TR), Vec<ApplyItemAction>)
|
) -> impl Fn((ItemStateProxy, TR), Vec<ApplyItemAction>)
|
||||||
-> BoxFuture<Result<((ItemStateProxy, TR), CharacterEntity), ItemStateError>>
|
-> BoxFuture<Result<((ItemStateProxy, TR), CharacterEntity), anyhow::Error>>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use anyhow::Context;
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
use rand::distributions::{WeightedIndex, Distribution};
|
use rand::distributions::{WeightedIndex, Distribution};
|
||||||
use crate::entity::gateway::{EntityGateway, GatewayError};
|
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||||
use crate::entity::character::CharacterEntity;
|
use crate::entity::character::{CharacterEntity, TechLevel};
|
||||||
use crate::entity::item::mag::{MagCell, MagCellError};
|
use crate::entity::item::mag::{MagCell, MagCellError};
|
||||||
use crate::entity::item::tool::{Tool, ToolType};
|
use crate::entity::item::tool::{Tool, ToolType};
|
||||||
|
use crate::entity::item::tech::TechniqueDisk;
|
||||||
use crate::entity::item::{ItemDetail, ItemEntityId};
|
use crate::entity::item::{ItemDetail, ItemEntityId};
|
||||||
use crate::ship::items::state::{ItemStateProxy, ItemStateError};
|
use crate::ship::items::state::ItemStateProxy;
|
||||||
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
|
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
|
||||||
|
|
||||||
|
|
||||||
@ -18,14 +20,12 @@ pub enum ApplyItemError {
|
|||||||
NoCharacter,
|
NoCharacter,
|
||||||
#[error("item not equipped")]
|
#[error("item not equipped")]
|
||||||
ItemNotEquipped,
|
ItemNotEquipped,
|
||||||
#[error("invalid item")]
|
#[error("could not use item invalid item")]
|
||||||
InvalidItem,
|
InvalidItem,
|
||||||
|
#[error("invalid tool")]
|
||||||
|
InvalidTool,
|
||||||
#[error("gateway error {0}")]
|
#[error("gateway error {0}")]
|
||||||
GatewayError(#[from] GatewayError),
|
GatewayError(#[from] GatewayError),
|
||||||
|
|
||||||
#[error("itemstate error {0}")]
|
|
||||||
ItemStateError(Box<ItemStateError>),
|
|
||||||
|
|
||||||
#[error("magcell error {0}")]
|
#[error("magcell error {0}")]
|
||||||
MagCellError(#[from] MagCellError),
|
MagCellError(#[from] MagCellError),
|
||||||
}
|
}
|
||||||
@ -38,49 +38,43 @@ pub enum ApplyItemAction {
|
|||||||
//RemoveItem,
|
//RemoveItem,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ItemStateError> for ApplyItemError {
|
async fn power_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
|
||||||
fn from(other: ItemStateError) -> ApplyItemError {
|
|
||||||
ApplyItemError::ItemStateError(Box::new(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn power_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
|
|
||||||
character.materials.power += 1;
|
character.materials.power += 1;
|
||||||
entity_gateway.save_character(character).await?;
|
entity_gateway.save_character(character).await?;
|
||||||
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mind_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
|
async fn mind_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
|
||||||
character.materials.mind += 1;
|
character.materials.mind += 1;
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn evade_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
|
async fn evade_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
|
||||||
character.materials.evade += 1;
|
character.materials.evade += 1;
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn def_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
|
async fn def_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
|
||||||
character.materials.def += 1;
|
character.materials.def += 1;
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn luck_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
|
async fn luck_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
|
||||||
character.materials.luck += 1;
|
character.materials.luck += 1;
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn hp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
|
async fn hp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
|
||||||
character.materials.hp += 1;
|
character.materials.hp += 1;
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
|
async fn tp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, anyhow::Error> {
|
||||||
character.materials.tp += 1;
|
character.materials.tp += 1;
|
||||||
entity_gateway.save_character(character).await.unwrap();
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
||||||
@ -113,7 +107,7 @@ async fn mag_cell<'a, EG>(item_state: &mut ItemStateProxy,
|
|||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
cell_entity_id: ItemEntityId,
|
cell_entity_id: ItemEntityId,
|
||||||
mag_cell_type: MagCell)
|
mag_cell_type: MagCell)
|
||||||
-> Result<Vec<ApplyItemAction>, ApplyItemError>
|
-> Result<Vec<ApplyItemAction>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + ?Sized,
|
EG: EntityGateway + ?Sized,
|
||||||
{
|
{
|
||||||
@ -229,7 +223,7 @@ pub async fn liberta_kit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
fn jack_o_lantern() -> Result<Vec<ApplyItemAction>, ApplyItemError>
|
fn jack_o_lantern() -> Result<Vec<ApplyItemAction>, anyhow::Error>
|
||||||
{
|
{
|
||||||
let mag_rate = WeightedIndex::new(&[13, 13, 13, 13, 12, 12, 12, 12]).unwrap();
|
let mag_rate = WeightedIndex::new(&[13, 13, 13, 13, 12, 12, 12, 12]).unwrap();
|
||||||
let mag_type = match mag_rate.sample(&mut rand_chacha::ChaChaRng::from_entropy()) {
|
let mag_type = match mag_rate.sample(&mut rand_chacha::ChaChaRng::from_entropy()) {
|
||||||
@ -252,7 +246,7 @@ async fn apply_tool<'a, EG>(item_state: &mut ItemStateProxy,
|
|||||||
character: &mut CharacterEntity,
|
character: &mut CharacterEntity,
|
||||||
entity_id: ItemEntityId,
|
entity_id: ItemEntityId,
|
||||||
tool: ToolType)
|
tool: ToolType)
|
||||||
-> Result<Vec<ApplyItemAction>, ApplyItemError>
|
-> Result<Vec<ApplyItemAction>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + ?Sized,
|
EG: EntityGateway + ?Sized,
|
||||||
{
|
{
|
||||||
@ -270,6 +264,13 @@ where
|
|||||||
ToolType::Monofluid => Ok(Vec::new()),
|
ToolType::Monofluid => Ok(Vec::new()),
|
||||||
ToolType::Difluid => Ok(Vec::new()),
|
ToolType::Difluid => Ok(Vec::new()),
|
||||||
ToolType::Trifluid => Ok(Vec::new()),
|
ToolType::Trifluid => Ok(Vec::new()),
|
||||||
|
ToolType::SolAtomizer => Ok(Vec::new()),
|
||||||
|
ToolType::MoonAtomizer => Ok(Vec::new()),
|
||||||
|
ToolType::StarAtomizer => Ok(Vec::new()),
|
||||||
|
ToolType::Telepipe => Ok(Vec::new()),
|
||||||
|
ToolType::Antidote => Ok(Vec::new()),
|
||||||
|
ToolType::Antiparalysis => Ok(Vec::new()),
|
||||||
|
ToolType::TrapVision => Ok(Vec::new()),
|
||||||
ToolType::HuntersReport => Ok(Vec::new()),
|
ToolType::HuntersReport => Ok(Vec::new()),
|
||||||
ToolType::CellOfMag502
|
ToolType::CellOfMag502
|
||||||
| ToolType::CellOfMag213
|
| ToolType::CellOfMag213
|
||||||
@ -299,17 +300,36 @@ where
|
|||||||
}
|
}
|
||||||
ToolType::JackOLantern => jack_o_lantern(),
|
ToolType::JackOLantern => jack_o_lantern(),
|
||||||
// TODO: rest of these
|
// TODO: rest of these
|
||||||
_ => Err(ApplyItemError::InvalidItem)
|
_ => Err(anyhow::Error::from(ApplyItemError::InvalidTool))
|
||||||
|
.with_context(|| {
|
||||||
|
format!("invalid tool {tool:?}")
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn apply_tech<'a, EG>(_item_state: &mut ItemStateProxy,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
character: &mut CharacterEntity,
|
||||||
|
_entity_id: ItemEntityId,
|
||||||
|
tech: TechniqueDisk)
|
||||||
|
-> Result<Vec<ApplyItemAction>, anyhow::Error>
|
||||||
|
where
|
||||||
|
EG: EntityGateway + ?Sized,
|
||||||
|
{
|
||||||
|
// TODO: make sure the class can learn that specific tech
|
||||||
|
character.techs.set_tech(tech.tech, TechLevel(tech.level as u8));
|
||||||
|
entity_gateway.save_character(character).await.unwrap();
|
||||||
|
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn apply_item<'a, EG>(item_state: &mut ItemStateProxy,
|
pub async fn apply_item<'a, EG>(item_state: &mut ItemStateProxy,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
character: &mut CharacterEntity,
|
character: &mut CharacterEntity,
|
||||||
item: InventoryItem
|
item: InventoryItem
|
||||||
) -> Result<Vec<ApplyItemAction>, ApplyItemError>
|
) -> Result<Vec<ApplyItemAction>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + ?Sized + Clone + 'static
|
EG: EntityGateway + ?Sized + Clone + 'static
|
||||||
{
|
{
|
||||||
@ -317,7 +337,11 @@ where
|
|||||||
InventoryItemDetail::Individual(individual_item) => {
|
InventoryItemDetail::Individual(individual_item) => {
|
||||||
match individual_item.item {
|
match individual_item.item {
|
||||||
ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, individual_item.entity_id, tool.tool).await,
|
ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, individual_item.entity_id, tool.tool).await,
|
||||||
_ => Err(ApplyItemError::InvalidItem)
|
ItemDetail::TechniqueDisk(tech) => apply_tech(item_state, entity_gateway, character, individual_item.entity_id, tech).await,
|
||||||
|
_ => Err(anyhow::Error::from(ApplyItemError::InvalidItem))
|
||||||
|
.with_context(|| {
|
||||||
|
format!("item {individual_item:?}")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
InventoryItemDetail::Stacked(stacked_item) => {
|
InventoryItemDetail::Stacked(stacked_item) => {
|
||||||
|
@ -64,10 +64,10 @@ pub struct BankItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BankItem {
|
impl BankItem {
|
||||||
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
|
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
F: FnMut(T, ItemEntityId) -> Fut,
|
F: FnMut(T, ItemEntityId) -> Fut,
|
||||||
Fut: Future<Output=Result<T, ItemStateError>>,
|
Fut: Future<Output=Result<T, anyhow::Error>>,
|
||||||
{
|
{
|
||||||
match &self.item {
|
match &self.item {
|
||||||
BankItemDetail::Individual(individual_item) => {
|
BankItemDetail::Individual(individual_item) => {
|
||||||
@ -125,27 +125,27 @@ impl BankState {
|
|||||||
self.item_id_counter = base_item_id + self.bank.0.len() as u32;
|
self.item_id_counter = base_item_id + self.bank.0.len() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
|
pub fn add_meseta(&mut self, amount: u32) -> Result<(), anyhow::Error> {
|
||||||
if self.meseta.0 + amount > 999999 {
|
if self.meseta.0 + amount > 999999 {
|
||||||
return Err(ItemStateError::FullOfMeseta)
|
return Err(ItemStateError::FullOfMeseta.into())
|
||||||
}
|
}
|
||||||
self.meseta.0 += amount;
|
self.meseta.0 += amount;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
|
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), anyhow::Error> {
|
||||||
if amount > self.meseta.0 {
|
if amount > self.meseta.0 {
|
||||||
return Err(ItemStateError::InvalidMesetaRemoval(amount))
|
return Err(ItemStateError::InvalidMesetaRemoval(amount).into())
|
||||||
}
|
}
|
||||||
self.meseta.0 -= amount;
|
self.meseta.0 -= amount;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result<AddItemResult, BankError> {
|
pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result<AddItemResult, anyhow::Error> {
|
||||||
match item.item {
|
match item.item {
|
||||||
InventoryItemDetail::Individual(iitem) => {
|
InventoryItemDetail::Individual(iitem) => {
|
||||||
if self.bank.0.len() >= 30 {
|
if self.bank.0.len() >= 30 {
|
||||||
Err(BankError::BankFull)
|
Err(BankError::BankFull.into())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.bank.0.push(BankItem {
|
self.bank.0.push(BankItem {
|
||||||
@ -166,7 +166,7 @@ impl BankState {
|
|||||||
match existing_stack {
|
match existing_stack {
|
||||||
Some(existing_stack) => {
|
Some(existing_stack) => {
|
||||||
if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
|
if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
|
||||||
Err(BankError::StackFull)
|
Err(BankError::StackFull.into())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
|
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
|
||||||
@ -175,7 +175,7 @@ impl BankState {
|
|||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
if self.bank.0.len() >= 30 {
|
if self.bank.0.len() >= 30 {
|
||||||
Err(BankError::BankFull)
|
Err(BankError::BankFull.into())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.bank.0.push(BankItem {
|
self.bank.0.push(BankItem {
|
||||||
|
@ -33,7 +33,7 @@ pub struct FloorItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FloorItem {
|
impl FloorItem {
|
||||||
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
|
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
F: FnMut(T, ItemEntityId) -> Fut,
|
F: FnMut(T, ItemEntityId) -> Fut,
|
||||||
Fut: Future<Output=Result<T, ItemStateError>>,
|
Fut: Future<Output=Result<T, ItemStateError>>,
|
||||||
@ -53,10 +53,10 @@ impl FloorItem {
|
|||||||
Ok(param)
|
Ok(param)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
|
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
F: FnMut(T, ItemEntityId, Mag) -> Fut,
|
F: FnMut(T, ItemEntityId, Mag) -> Fut,
|
||||||
Fut: Future<Output=Result<T, ItemStateError>>,
|
Fut: Future<Output=Result<T, anyhow::Error>>,
|
||||||
{
|
{
|
||||||
if let FloorItemDetail::Individual(individual_item) = &self.item {
|
if let FloorItemDetail::Individual(individual_item) = &self.item {
|
||||||
if let ItemDetail::Mag(mag) = &individual_item.item {
|
if let ItemDetail::Mag(mag) = &individual_item.item {
|
||||||
|
@ -60,7 +60,7 @@ impl InventoryItemDetail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this should probably go somewhere a bit more fundamental like ItemDetail
|
// TODO: this should probably go somewhere a bit more fundamental like ItemDetail
|
||||||
pub fn sell_price(&self) -> Result<u32, ItemStateError> {
|
pub fn sell_price(&self) -> Result<u32, anyhow::Error> {
|
||||||
match self {
|
match self {
|
||||||
InventoryItemDetail::Individual(individual_item) => {
|
InventoryItemDetail::Individual(individual_item) => {
|
||||||
match &individual_item.item {
|
match &individual_item.item {
|
||||||
@ -102,7 +102,7 @@ impl InventoryItemDetail {
|
|||||||
Ok((ToolShopItem::from(d).price() / 8) as u32)
|
Ok((ToolShopItem::from(d).price() / 8) as u32)
|
||||||
},
|
},
|
||||||
ItemDetail::Mag(_m) => {
|
ItemDetail::Mag(_m) => {
|
||||||
Err(ItemStateError::ItemNotSellable)
|
Err(ItemStateError::ItemNotSellable.into())
|
||||||
},
|
},
|
||||||
ItemDetail::ESWeapon(_e) => {
|
ItemDetail::ESWeapon(_e) => {
|
||||||
Ok(10u32)
|
Ok(10u32)
|
||||||
@ -126,10 +126,10 @@ pub struct InventoryItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InventoryItem {
|
impl InventoryItem {
|
||||||
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
|
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
F: FnMut(T, ItemEntityId) -> Fut,
|
F: FnMut(T, ItemEntityId) -> Fut,
|
||||||
Fut: Future<Output=Result<T, ItemStateError>>,
|
Fut: Future<Output=Result<T, anyhow::Error>>,
|
||||||
{
|
{
|
||||||
match &self.item {
|
match &self.item {
|
||||||
InventoryItemDetail::Individual(individual_item) => {
|
InventoryItemDetail::Individual(individual_item) => {
|
||||||
@ -145,10 +145,10 @@ impl InventoryItem {
|
|||||||
Ok(param)
|
Ok(param)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
|
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
F: FnMut(T, ItemEntityId, Mag) -> Fut,
|
F: FnMut(T, ItemEntityId, Mag) -> Fut,
|
||||||
Fut: Future<Output=Result<T, ItemStateError>>,
|
Fut: Future<Output=Result<T, anyhow::Error>>,
|
||||||
{
|
{
|
||||||
if let InventoryItemDetail::Individual(individual_item) = &self.item {
|
if let InventoryItemDetail::Individual(individual_item) = &self.item {
|
||||||
if let ItemDetail::Mag(mag) = &individual_item.item {
|
if let ItemDetail::Mag(mag) = &individual_item.item {
|
||||||
@ -205,11 +205,11 @@ impl InventoryState {
|
|||||||
self.inventory.0.len()
|
self.inventory.0.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_floor_item(&mut self, item: FloorItem) -> Result<AddItemResult, InventoryError> {
|
pub fn add_floor_item(&mut self, item: FloorItem) -> Result<AddItemResult, anyhow::Error> {
|
||||||
match item.item {
|
match item.item {
|
||||||
FloorItemDetail::Individual(iitem) => {
|
FloorItemDetail::Individual(iitem) => {
|
||||||
if self.inventory.0.len() >= 30 {
|
if self.inventory.0.len() >= 30 {
|
||||||
Err(InventoryError::InventoryFull)
|
Err(InventoryError::InventoryFull.into())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.inventory.0.push(InventoryItem {
|
self.inventory.0.push(InventoryItem {
|
||||||
@ -229,7 +229,7 @@ impl InventoryState {
|
|||||||
match existing_stack {
|
match existing_stack {
|
||||||
Some(existing_stack) => {
|
Some(existing_stack) => {
|
||||||
if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
|
if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
|
||||||
Err(InventoryError::StackFull)
|
Err(InventoryError::StackFull.into())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
|
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
|
||||||
@ -238,7 +238,7 @@ impl InventoryState {
|
|||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
if self.inventory.0.len() >= 30 {
|
if self.inventory.0.len() >= 30 {
|
||||||
Err(InventoryError::InventoryFull)
|
Err(InventoryError::InventoryFull.into())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.inventory.0.push(InventoryItem {
|
self.inventory.0.push(InventoryItem {
|
||||||
@ -253,7 +253,7 @@ impl InventoryState {
|
|||||||
},
|
},
|
||||||
FloorItemDetail::Meseta(meseta) => {
|
FloorItemDetail::Meseta(meseta) => {
|
||||||
if self.meseta == Meseta(999999) {
|
if self.meseta == Meseta(999999) {
|
||||||
Err(InventoryError::MesetaFull)
|
Err(InventoryError::MesetaFull.into())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0, 999999);
|
self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0, 999999);
|
||||||
@ -263,11 +263,11 @@ impl InventoryState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_item(&mut self, item: InventoryItem) -> Result<(AddItemResult, InventoryItem), InventoryError> {
|
pub fn add_item(&mut self, item: InventoryItem) -> Result<(AddItemResult, InventoryItem), anyhow::Error> {
|
||||||
match &item.item {
|
match &item.item {
|
||||||
InventoryItemDetail::Individual(_) => {
|
InventoryItemDetail::Individual(_) => {
|
||||||
if self.inventory.0.len() >= 30 {
|
if self.inventory.0.len() >= 30 {
|
||||||
Err(InventoryError::InventoryFull)
|
Err(InventoryError::InventoryFull.into())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.inventory.0.push(item);
|
self.inventory.0.push(item);
|
||||||
@ -290,7 +290,7 @@ impl InventoryState {
|
|||||||
match existing_stack {
|
match existing_stack {
|
||||||
Some(existing_stack) => {
|
Some(existing_stack) => {
|
||||||
if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
|
if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
|
||||||
Err(InventoryError::StackFull)
|
Err(InventoryError::StackFull.into())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
|
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
|
||||||
@ -307,7 +307,7 @@ impl InventoryState {
|
|||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
if self.inventory.0.len() >= 30 {
|
if self.inventory.0.len() >= 30 {
|
||||||
Err(InventoryError::InventoryFull)
|
Err(InventoryError::InventoryFull.into())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.inventory.0.push(item);
|
self.inventory.0.push(item);
|
||||||
@ -370,25 +370,25 @@ impl InventoryState {
|
|||||||
.find(|i| i.item_id == *item_id)
|
.find(|i| i.item_id == *item_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
|
pub fn add_meseta(&mut self, amount: u32) -> Result<(), anyhow::Error> {
|
||||||
if self.meseta.0 == 999999 {
|
if self.meseta.0 == 999999 {
|
||||||
return Err(ItemStateError::FullOfMeseta)
|
return Err(ItemStateError::FullOfMeseta.into())
|
||||||
}
|
}
|
||||||
self.meseta.0 = std::cmp::min(self.meseta.0 + amount, 999999);
|
self.meseta.0 = std::cmp::min(self.meseta.0 + amount, 999999);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), ItemStateError> {
|
pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), anyhow::Error> {
|
||||||
if self.meseta.0 + amount > 999999 {
|
if self.meseta.0 + amount > 999999 {
|
||||||
return Err(ItemStateError::FullOfMeseta)
|
return Err(ItemStateError::FullOfMeseta.into())
|
||||||
}
|
}
|
||||||
self.meseta.0 += amount;
|
self.meseta.0 += amount;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
|
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), anyhow::Error> {
|
||||||
if amount > self.meseta.0 {
|
if amount > self.meseta.0 {
|
||||||
return Err(ItemStateError::InvalidMesetaRemoval(amount))
|
return Err(ItemStateError::InvalidMesetaRemoval(amount).into())
|
||||||
}
|
}
|
||||||
self.meseta.0 -= amount;
|
self.meseta.0 -= amount;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use async_std::sync::{Arc, RwLock, Mutex};
|
use async_std::sync::{Arc, RwLock, Mutex};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
|
use anyhow::Context;
|
||||||
|
|
||||||
use crate::entity::gateway::{EntityGateway, GatewayError};
|
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||||
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||||
@ -21,6 +22,8 @@ pub enum ItemStateError {
|
|||||||
NoCharacter(CharacterEntityId),
|
NoCharacter(CharacterEntityId),
|
||||||
#[error("room {0} not found")]
|
#[error("room {0} not found")]
|
||||||
NoRoom(RoomId),
|
NoRoom(RoomId),
|
||||||
|
#[error("inventory item {0} not found")]
|
||||||
|
NoInventoryItem(ClientItemId),
|
||||||
#[error("floor item {0} not found")]
|
#[error("floor item {0} not found")]
|
||||||
NoFloorItem(ClientItemId),
|
NoFloorItem(ClientItemId),
|
||||||
#[error("expected {0} to be a tool")]
|
#[error("expected {0} to be a tool")]
|
||||||
@ -55,7 +58,7 @@ pub enum ItemStateError {
|
|||||||
ItemNotSellable,
|
ItemNotSellable,
|
||||||
#[error("could not modify item")]
|
#[error("could not modify item")]
|
||||||
InvalidModifier,
|
InvalidModifier,
|
||||||
#[error("wrong item type ")]
|
#[error("wrong item type {0}")]
|
||||||
WrongItemType(ClientItemId),
|
WrongItemType(ClientItemId),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +153,7 @@ impl Default for ItemState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ItemState {
|
impl ItemState {
|
||||||
pub async fn get_character_inventory(&self, character: &CharacterEntity) -> Result<InventoryState, ItemStateError> {
|
pub async fn get_character_inventory(&self, character: &CharacterEntity) -> Result<InventoryState, anyhow::Error> {
|
||||||
Ok(self.character_inventory
|
Ok(self.character_inventory
|
||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
@ -161,7 +164,7 @@ impl ItemState {
|
|||||||
.clone())
|
.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_character_bank(&self, character: &CharacterEntity) -> Result<BankState, ItemStateError> {
|
pub async fn get_character_bank(&self, character: &CharacterEntity) -> Result<BankState, anyhow::Error> {
|
||||||
Ok(self.character_bank
|
Ok(self.character_bank
|
||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
@ -174,20 +177,20 @@ impl ItemState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ItemState {
|
impl ItemState {
|
||||||
async fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> {
|
async fn new_item_id(&mut self) -> Result<ClientItemId, anyhow::Error> {
|
||||||
*self.room_item_id_counter
|
*self.room_item_id_counter
|
||||||
.write()
|
.write()
|
||||||
.await += 1;
|
.await += 1;
|
||||||
Ok(ClientItemId(*self.room_item_id_counter.read().await))
|
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> {
|
pub async fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), anyhow::Error> {
|
||||||
let inventory = entity_gateway.get_character_inventory(&character.id).await?;
|
let inventory = entity_gateway.get_character_inventory(&character.id).await?;
|
||||||
let bank = entity_gateway.get_character_bank(&character.id, &BankName("".into())).await?;
|
let bank = entity_gateway.get_character_bank(&character.id, &BankName("".into())).await?;
|
||||||
let equipped = entity_gateway.get_character_equips(&character.id).await?;
|
let equipped = entity_gateway.get_character_equips(&character.id).await?;
|
||||||
|
|
||||||
let inventory_items = inventory.items.into_iter()
|
let inventory_items = inventory.items.into_iter()
|
||||||
.map(|item| -> Result<InventoryItem, ItemStateError> {
|
.map(|item| -> Result<InventoryItem, anyhow::Error> {
|
||||||
Ok(match item {
|
Ok(match item {
|
||||||
InventoryItemEntity::Individual(item) => {
|
InventoryItemEntity::Individual(item) => {
|
||||||
InventoryItem {
|
InventoryItem {
|
||||||
@ -214,7 +217,7 @@ impl ItemState {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, ItemStateError>>()?;
|
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
||||||
|
|
||||||
let character_meseta = entity_gateway.get_character_meseta(&character.id).await?;
|
let character_meseta = entity_gateway.get_character_meseta(&character.id).await?;
|
||||||
let inventory_state = InventoryState {
|
let inventory_state = InventoryState {
|
||||||
@ -259,7 +262,7 @@ impl ItemState {
|
|||||||
.collect::<Vec<_>>())
|
.collect::<Vec<_>>())
|
||||||
.await
|
.await
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Result<Vec<_>, ItemStateError>>()?;
|
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
||||||
|
|
||||||
let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?;
|
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);
|
let bank_state = BankState::new(character.id, BankName("".into()), Bank::new(bank_items), bank_meseta);
|
||||||
@ -334,7 +337,7 @@ impl ItemState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_floor_item(&self, character_id: &CharacterEntityId, item_id: &ClientItemId) -> Result<(FloorItem, FloorType), ItemStateError> {
|
pub async fn get_floor_item(&self, character_id: &CharacterEntityId, item_id: &ClientItemId) -> Result<(FloorItem, FloorType), anyhow::Error> {
|
||||||
let local_floors = self.character_floor
|
let local_floors = self.character_floor
|
||||||
.read()
|
.read()
|
||||||
.await;
|
.await;
|
||||||
@ -369,6 +372,7 @@ impl ItemState {
|
|||||||
.map(|item| (item.clone(), FloorType::Shared))
|
.map(|item| (item.clone(), FloorType::Shared))
|
||||||
})
|
})
|
||||||
.ok_or_else(|| ItemStateError::NoFloorItem(*item_id))
|
.ok_or_else(|| ItemStateError::NoFloorItem(*item_id))
|
||||||
|
.with_context(|| format!("character {character_id}\nlocal floors: {local_floors:#?}\nshared floors: {shared_floors:#?}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,7 +425,7 @@ impl ItemStateProxy {
|
|||||||
async fn get_or_clone<K, V>(master: &Arc<RwLock<HashMap<K, RwLock<V>>>>,
|
async fn get_or_clone<K, V>(master: &Arc<RwLock<HashMap<K, RwLock<V>>>>,
|
||||||
proxy: &Arc<Mutex<HashMap<K, V>>>,
|
proxy: &Arc<Mutex<HashMap<K, V>>>,
|
||||||
key: K,
|
key: K,
|
||||||
err: fn(K) -> ItemStateError) -> Result<V, ItemStateError>
|
err: fn(K) -> ItemStateError) -> Result<V, anyhow::Error>
|
||||||
where
|
where
|
||||||
K: Eq + std::hash::Hash + Copy,
|
K: Eq + std::hash::Hash + Copy,
|
||||||
V: Clone
|
V: Clone
|
||||||
@ -451,7 +455,7 @@ impl ItemStateProxy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn inventory(&mut self, character_id: &CharacterEntityId) -> Result<InventoryState, ItemStateError> {
|
pub async fn inventory(&mut self, character_id: &CharacterEntityId) -> Result<InventoryState, anyhow::Error> {
|
||||||
get_or_clone(&self.item_state.character_inventory,
|
get_or_clone(&self.item_state.character_inventory,
|
||||||
&self.proxied_state.character_inventory,
|
&self.proxied_state.character_inventory,
|
||||||
*character_id,
|
*character_id,
|
||||||
@ -462,7 +466,7 @@ impl ItemStateProxy {
|
|||||||
self.proxied_state.character_inventory.lock().await.insert(inventory.character_id, inventory);
|
self.proxied_state.character_inventory.lock().await.insert(inventory.character_id, inventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn bank(&mut self, character_id: &CharacterEntityId) -> Result<BankState, ItemStateError> {
|
pub async fn bank(&mut self, character_id: &CharacterEntityId) -> Result<BankState, anyhow::Error> {
|
||||||
get_or_clone(&self.item_state.character_bank,
|
get_or_clone(&self.item_state.character_bank,
|
||||||
&self.proxied_state.character_bank,
|
&self.proxied_state.character_bank,
|
||||||
*character_id,
|
*character_id,
|
||||||
@ -473,7 +477,7 @@ impl ItemStateProxy {
|
|||||||
self.proxied_state.character_bank.lock().await.insert(bank.character_id, bank);
|
self.proxied_state.character_bank.lock().await.insert(bank.character_id, bank);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn floor(&mut self, character_id: &CharacterEntityId) -> Result<FloorState, ItemStateError> {
|
pub async fn floor(&mut self, character_id: &CharacterEntityId) -> Result<FloorState, anyhow::Error> {
|
||||||
let room_id = *self.item_state.character_room.read().await.get(character_id).unwrap();
|
let room_id = *self.item_state.character_room.read().await.get(character_id).unwrap();
|
||||||
Ok(FloorState {
|
Ok(FloorState {
|
||||||
character_id: *character_id,
|
character_id: *character_id,
|
||||||
@ -488,7 +492,7 @@ impl ItemStateProxy {
|
|||||||
self.proxied_state.room_floor.lock().await.insert(room_id, floor.shared);
|
self.proxied_state.room_floor.lock().await.insert(room_id, floor.shared);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_item_id(&mut self) -> Result<ClientItemId, ItemStateError> {
|
pub async fn new_item_id(&mut self) -> Result<ClientItemId, anyhow::Error> {
|
||||||
self.item_state.new_item_id().await
|
self.item_state.new_item_id().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use crate::ship::ship::SendShipPacket;
|
|||||||
use crate::ship::map::MapArea;
|
use crate::ship::map::MapArea;
|
||||||
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
use crate::entity::character::{CharacterEntity, CharacterEntityId};
|
||||||
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction};
|
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction};
|
||||||
use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, IndividualItemDetail};
|
use crate::ship::items::state::{ItemState, ItemStateProxy, IndividualItemDetail};
|
||||||
use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction};
|
use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction};
|
||||||
use crate::ship::items::inventory::InventoryItem;
|
use crate::ship::items::inventory::InventoryItem;
|
||||||
use crate::ship::items::floor::FloorItem;
|
use crate::ship::items::floor::FloorItem;
|
||||||
@ -22,7 +22,7 @@ pub async fn pick_up_item<EG>(
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
item_id: &ClientItemId)
|
item_id: &ClientItemId)
|
||||||
-> Result<actions::TriggerCreateItem, ItemStateError>
|
-> Result<actions::TriggerCreateItem, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
EG::Transaction: Clone,
|
EG::Transaction: Clone,
|
||||||
@ -46,7 +46,7 @@ pub async fn drop_item<EG>(
|
|||||||
item_id: &ClientItemId,
|
item_id: &ClientItemId,
|
||||||
map_area: MapArea,
|
map_area: MapArea,
|
||||||
drop_position: (f32, f32, f32))
|
drop_position: (f32, f32, f32))
|
||||||
-> Result<FloorItem, ItemStateError>
|
-> Result<FloorItem, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -70,7 +70,7 @@ pub async fn drop_partial_item<'a, EG>(
|
|||||||
map_area: MapArea,
|
map_area: MapArea,
|
||||||
drop_position: (f32, f32),
|
drop_position: (f32, f32),
|
||||||
amount: u32)
|
amount: u32)
|
||||||
-> Result<FloorItem, ItemStateError>
|
-> Result<FloorItem, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -95,7 +95,7 @@ pub async fn drop_meseta<'a, EG>(
|
|||||||
map_area: MapArea,
|
map_area: MapArea,
|
||||||
drop_position: (f32, f32),
|
drop_position: (f32, f32),
|
||||||
amount: u32)
|
amount: u32)
|
||||||
-> Result<FloorItem, ItemStateError>
|
-> Result<FloorItem, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -117,7 +117,7 @@ pub async fn withdraw_meseta<'a, EG>(
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
amount: u32)
|
amount: u32)
|
||||||
-> Result<(), ItemStateError>
|
-> Result<(), anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -139,7 +139,7 @@ pub async fn deposit_meseta<'a, EG>(
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
amount: u32)
|
amount: u32)
|
||||||
-> Result<(), ItemStateError>
|
-> Result<(), anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -162,7 +162,7 @@ pub async fn withdraw_item<'a, EG>(
|
|||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
item_id: &ClientItemId,
|
item_id: &ClientItemId,
|
||||||
amount: u32)
|
amount: u32)
|
||||||
-> Result<InventoryItem, ItemStateError>
|
-> Result<InventoryItem, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -187,7 +187,7 @@ pub async fn deposit_item<'a, EG> (
|
|||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
item_id: &ClientItemId,
|
item_id: &ClientItemId,
|
||||||
amount: u32)
|
amount: u32)
|
||||||
-> Result<(), ItemStateError>
|
-> Result<(), anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -209,7 +209,7 @@ pub async fn equip_item<'a, EG> (
|
|||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
item_id: &ClientItemId,
|
item_id: &ClientItemId,
|
||||||
equip_slot: u8,
|
equip_slot: u8,
|
||||||
) -> Result<(), ItemStateError>
|
) -> Result<(), anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -230,7 +230,7 @@ pub async fn unequip_item<'a, EG> (
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
item_id: &ClientItemId,
|
item_id: &ClientItemId,
|
||||||
) -> Result<(), ItemStateError>
|
) -> Result<(), anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -251,7 +251,7 @@ pub async fn sort_inventory<'a, EG> (
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
item_ids: Vec<ClientItemId>,
|
item_ids: Vec<ClientItemId>,
|
||||||
) -> Result<(), ItemStateError>
|
) -> Result<(), anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -274,7 +274,7 @@ pub async fn use_item<'a, EG> (
|
|||||||
area_client: AreaClient,
|
area_client: AreaClient,
|
||||||
item_id: &ClientItemId,
|
item_id: &ClientItemId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> Result<Vec<SendShipPacket>, ItemStateError>
|
) -> Result<Vec<SendShipPacket>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -303,7 +303,7 @@ pub async fn feed_mag<'a, EG> (
|
|||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
mag_item_id: &ClientItemId,
|
mag_item_id: &ClientItemId,
|
||||||
tool_item_id: &ClientItemId,
|
tool_item_id: &ClientItemId,
|
||||||
) -> Result<(), ItemStateError>
|
) -> Result<(), anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -327,7 +327,7 @@ pub async fn buy_shop_item<'a, EG> (
|
|||||||
shop_item: &'a (dyn ShopItem + Send + Sync),
|
shop_item: &'a (dyn ShopItem + Send + Sync),
|
||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> Result<InventoryItem, ItemStateError>
|
) -> Result<InventoryItem, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -353,7 +353,7 @@ pub async fn sell_item<'a, EG> (
|
|||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> Result<InventoryItem, ItemStateError>
|
) -> Result<InventoryItem, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -373,7 +373,7 @@ pub async fn trade_items<'a, EG> (
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
p1: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta),
|
p1: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta),
|
||||||
p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta))
|
p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta))
|
||||||
-> Result<(Vec<InventoryItem>, Vec<InventoryItem>), ItemStateError>
|
-> Result<(Vec<InventoryItem>, Vec<InventoryItem>), anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -443,7 +443,7 @@ pub async fn take_meseta<'a, EG> (
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
character_id: &CharacterEntityId,
|
character_id: &CharacterEntityId,
|
||||||
meseta: Meseta)
|
meseta: Meseta)
|
||||||
-> Result<(), ItemStateError>
|
-> Result<(), anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -464,7 +464,7 @@ pub async fn enemy_drops_item<'a, EG> (
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
item_drop: ItemDrop)
|
item_drop: ItemDrop)
|
||||||
-> Result<FloorItem, ItemStateError>
|
-> Result<FloorItem, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -488,7 +488,7 @@ pub async fn apply_modifier<'a, EG> (
|
|||||||
character: &CharacterEntity,
|
character: &CharacterEntity,
|
||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
modifier: ItemModifier)
|
modifier: ItemModifier)
|
||||||
-> Result<IndividualItemDetail, ItemStateError>
|
-> Result<IndividualItemDetail, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
|
@ -30,41 +30,50 @@ impl LobbyId {
|
|||||||
|
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("create room")]
|
|
||||||
pub enum CreateRoomError {
|
pub enum CreateRoomError {
|
||||||
|
#[error("no open slots")]
|
||||||
NoOpenSlots,
|
NoOpenSlots,
|
||||||
|
#[error("client already in area")]
|
||||||
ClientInAreaAlready,
|
ClientInAreaAlready,
|
||||||
|
#[error("join error")]
|
||||||
JoinError,
|
JoinError,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("join room")]
|
|
||||||
pub enum JoinRoomError {
|
pub enum JoinRoomError {
|
||||||
|
#[error("room does not exist")]
|
||||||
RoomDoesNotExist,
|
RoomDoesNotExist,
|
||||||
|
#[error("room is full")]
|
||||||
RoomFull,
|
RoomFull,
|
||||||
|
#[error("client already in area")]
|
||||||
ClientInAreaAlready,
|
ClientInAreaAlready,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("join lobby")]
|
|
||||||
pub enum JoinLobbyError {
|
pub enum JoinLobbyError {
|
||||||
|
#[error("lobby does not exist")]
|
||||||
LobbyDoesNotExist,
|
LobbyDoesNotExist,
|
||||||
|
#[error("lobby is full")]
|
||||||
LobbyFull,
|
LobbyFull,
|
||||||
|
#[error("client already in area")]
|
||||||
ClientInAreaAlready,
|
ClientInAreaAlready,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("get area")]
|
|
||||||
pub enum GetAreaError {
|
pub enum GetAreaError {
|
||||||
|
#[error("not in a room")]
|
||||||
NotInRoom,
|
NotInRoom,
|
||||||
|
#[error("not in a lobby")]
|
||||||
NotInLobby,
|
NotInLobby,
|
||||||
|
#[error("get area: invalid client")]
|
||||||
InvalidClient,
|
InvalidClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("client removal")]
|
|
||||||
pub enum ClientRemovalError {
|
pub enum ClientRemovalError {
|
||||||
|
#[error("client removal: client not in area")]
|
||||||
ClientNotInArea,
|
ClientNotInArea,
|
||||||
|
#[error("client removal: invalid area")]
|
||||||
InvalidArea,
|
InvalidArea,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,17 +86,20 @@ pub enum GetClientsError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("get neighbor")]
|
|
||||||
pub enum GetNeighborError {
|
pub enum GetNeighborError {
|
||||||
|
#[error("get neighbor: invalid client")]
|
||||||
InvalidClient,
|
InvalidClient,
|
||||||
|
#[error("get neighbor: invalid area")]
|
||||||
InvalidArea,
|
InvalidArea,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq, Eq)]
|
#[derive(Error, Debug, PartialEq, Eq)]
|
||||||
#[error("get leader")]
|
|
||||||
pub enum GetLeaderError {
|
pub enum GetLeaderError {
|
||||||
|
#[error("get leader: invalid client")]
|
||||||
InvalidClient,
|
InvalidClient,
|
||||||
|
#[error("get leader: invalid area")]
|
||||||
InvalidArea,
|
InvalidArea,
|
||||||
|
#[error("get leader: client not in area")]
|
||||||
NoClientInArea,
|
NoClientInArea,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,9 +99,18 @@ impl RareMonsterAppearTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn roll_is_rare(&self, monster: &MonsterType) -> bool {
|
fn roll_is_rare(&self, monster: &MonsterType) -> bool {
|
||||||
rand_chacha::ChaChaRng::from_entropy().gen::<f32>() < *self.appear_rate.get(monster).unwrap_or(&0.0f32)
|
rand_chacha::ChaChaRng::from_entropy().gen::<f32>() < *self.appear_rate.get(monster).unwrap_or(&0.0f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn apply(&self, enemy: MapEnemy, event: ShipEvent) -> MapEnemy {
|
||||||
|
if enemy.can_be_rare() && self.roll_is_rare(&enemy.monster) {
|
||||||
|
enemy.into_rare(event)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
enemy
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,12 +8,13 @@ use thiserror::Error;
|
|||||||
|
|
||||||
use crate::ship::ship::ShipEvent;
|
use crate::ship::ship::ShipEvent;
|
||||||
use crate::ship::monster::MonsterType;
|
use crate::ship::monster::MonsterType;
|
||||||
use crate::ship::room::{Episode, RoomMode};
|
use crate::ship::room::{Episode, RoomMode, PlayerMode};
|
||||||
|
|
||||||
// TODO: don't use *
|
// TODO: don't use *
|
||||||
use crate::ship::map::*;
|
use crate::ship::map::*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec<Option<MapObject>> {
|
pub fn objects_from_stream(cursor: &mut impl Read, episode: &Episode, map_area: &MapArea) -> Vec<Option<MapObject>> {
|
||||||
let mut object_data = Vec::new();
|
let mut object_data = Vec::new();
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) ->
|
|||||||
enemy
|
enemy
|
||||||
.map_or(vec![None], |monster| {
|
.map_or(vec![None], |monster| {
|
||||||
let mut monsters = vec![Some(monster)];
|
let mut monsters = vec![Some(monster)];
|
||||||
|
|
||||||
match monster.monster {
|
match monster.monster {
|
||||||
MonsterType::Monest => {
|
MonsterType::Monest => {
|
||||||
for _ in 0..30 {
|
for _ in 0..30 {
|
||||||
@ -172,25 +173,10 @@ fn enemy_data_from_map_data(map_variant: &MapVariant, episode: &Episode) -> Vec<
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
pub fn default_map_variants(episode: Episode, player_mode: PlayerMode) -> Vec<MapVariant> {
|
||||||
#[error("")]
|
match (episode, player_mode) {
|
||||||
pub enum MapsError {
|
(Episode::One, PlayerMode::Multi) => {
|
||||||
InvalidMonsterId(usize),
|
vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Online),
|
||||||
InvalidObjectId(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Maps {
|
|
||||||
map_variants: Vec<MapVariant>,
|
|
||||||
enemy_data: Vec<Option<MapEnemy>>,
|
|
||||||
object_data: Vec<Option<MapObject>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Maps {
|
|
||||||
pub fn new(room_mode: RoomMode, rare_monster_table: &enemy::RareMonsterAppearTable, event: ShipEvent) -> Maps {
|
|
||||||
let map_variants = match (room_mode.episode(), room_mode.single_player()) {
|
|
||||||
(Episode::One, 0) => {
|
|
||||||
vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Online),
|
|
||||||
MapVariant::new(MapArea::Forest1, MapVariantMode::Online),
|
MapVariant::new(MapArea::Forest1, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::Forest2, MapVariantMode::Online),
|
MapVariant::new(MapArea::Forest2, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::Caves1, MapVariantMode::Online),
|
MapVariant::new(MapArea::Caves1, MapVariantMode::Online),
|
||||||
@ -205,10 +191,10 @@ impl Maps {
|
|||||||
MapVariant::new(MapArea::DeRolLe, MapVariantMode::Online),
|
MapVariant::new(MapArea::DeRolLe, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::VolOpt, MapVariantMode::Online),
|
MapVariant::new(MapArea::VolOpt, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::DarkFalz, MapVariantMode::Online),
|
MapVariant::new(MapArea::DarkFalz, MapVariantMode::Online),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
(Episode::One, 1) => {
|
(Episode::One, PlayerMode::Single) => {
|
||||||
vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Offline),
|
vec![MapVariant::new(MapArea::Pioneer2Ep1, MapVariantMode::Offline),
|
||||||
MapVariant::new(MapArea::Forest1, MapVariantMode::Offline),
|
MapVariant::new(MapArea::Forest1, MapVariantMode::Offline),
|
||||||
MapVariant::new(MapArea::Forest2, MapVariantMode::Offline),
|
MapVariant::new(MapArea::Forest2, MapVariantMode::Offline),
|
||||||
MapVariant::new(MapArea::Caves1, MapVariantMode::Offline),
|
MapVariant::new(MapArea::Caves1, MapVariantMode::Offline),
|
||||||
@ -223,10 +209,10 @@ impl Maps {
|
|||||||
MapVariant::new(MapArea::DeRolLe, MapVariantMode::Offline),
|
MapVariant::new(MapArea::DeRolLe, MapVariantMode::Offline),
|
||||||
MapVariant::new(MapArea::VolOpt, MapVariantMode::Offline),
|
MapVariant::new(MapArea::VolOpt, MapVariantMode::Offline),
|
||||||
MapVariant::new(MapArea::DarkFalz, MapVariantMode::Offline),
|
MapVariant::new(MapArea::DarkFalz, MapVariantMode::Offline),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
(Episode::Two, 0) => {
|
(Episode::Two, PlayerMode::Multi) => {
|
||||||
vec![MapVariant::new(MapArea::Pioneer2Ep2, MapVariantMode::Online),
|
vec![MapVariant::new(MapArea::Pioneer2Ep2, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::VrTempleAlpha, MapVariantMode::Online),
|
MapVariant::new(MapArea::VrTempleAlpha, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::VrTempleBeta, MapVariantMode::Online),
|
MapVariant::new(MapArea::VrTempleBeta, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::VrSpaceshipAlpha, MapVariantMode::Online),
|
MapVariant::new(MapArea::VrSpaceshipAlpha, MapVariantMode::Online),
|
||||||
@ -242,10 +228,10 @@ impl Maps {
|
|||||||
MapVariant::new(MapArea::OlgaFlow, MapVariantMode::Online),
|
MapVariant::new(MapArea::OlgaFlow, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::BarbaRay, MapVariantMode::Online),
|
MapVariant::new(MapArea::BarbaRay, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::GolDragon, MapVariantMode::Online),
|
MapVariant::new(MapArea::GolDragon, MapVariantMode::Online),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
(Episode::Two, 1) => {
|
(Episode::Two, PlayerMode::Single) => {
|
||||||
vec![MapVariant::new(MapArea::Pioneer2Ep2, MapVariantMode::Offline),
|
vec![MapVariant::new(MapArea::Pioneer2Ep2, MapVariantMode::Offline),
|
||||||
MapVariant::new(MapArea::VrTempleAlpha, MapVariantMode::Offline),
|
MapVariant::new(MapArea::VrTempleAlpha, MapVariantMode::Offline),
|
||||||
MapVariant::new(MapArea::VrTempleBeta, MapVariantMode::Offline),
|
MapVariant::new(MapArea::VrTempleBeta, MapVariantMode::Offline),
|
||||||
MapVariant::new(MapArea::VrSpaceshipAlpha, MapVariantMode::Offline),
|
MapVariant::new(MapArea::VrSpaceshipAlpha, MapVariantMode::Offline),
|
||||||
@ -261,10 +247,10 @@ impl Maps {
|
|||||||
MapVariant::new(MapArea::OlgaFlow, MapVariantMode::Offline),
|
MapVariant::new(MapArea::OlgaFlow, MapVariantMode::Offline),
|
||||||
MapVariant::new(MapArea::BarbaRay, MapVariantMode::Offline),
|
MapVariant::new(MapArea::BarbaRay, MapVariantMode::Offline),
|
||||||
MapVariant::new(MapArea::GolDragon, MapVariantMode::Offline),
|
MapVariant::new(MapArea::GolDragon, MapVariantMode::Offline),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
(Episode::Four, _) => {
|
(Episode::Four, PlayerMode::Multi) => {
|
||||||
vec![MapVariant::new(MapArea::Pioneer2Ep4, MapVariantMode::Online),
|
vec![MapVariant::new(MapArea::Pioneer2Ep4, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::CraterEast, MapVariantMode::Online),
|
MapVariant::new(MapArea::CraterEast, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::CraterWest, MapVariantMode::Online),
|
MapVariant::new(MapArea::CraterWest, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::CraterSouth, MapVariantMode::Online),
|
MapVariant::new(MapArea::CraterSouth, MapVariantMode::Online),
|
||||||
@ -274,23 +260,44 @@ impl Maps {
|
|||||||
MapVariant::new(MapArea::SubDesert2, MapVariantMode::Online),
|
MapVariant::new(MapArea::SubDesert2, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::SubDesert3, MapVariantMode::Online),
|
MapVariant::new(MapArea::SubDesert3, MapVariantMode::Online),
|
||||||
MapVariant::new(MapArea::SaintMillion, MapVariantMode::Online),
|
MapVariant::new(MapArea::SaintMillion, MapVariantMode::Online),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
_ => unreachable!()
|
(Episode::Four, PlayerMode::Single) => {
|
||||||
};
|
vec![MapVariant::new(MapArea::Pioneer2Ep4, MapVariantMode::Offline),
|
||||||
|
MapVariant::new(MapArea::CraterEast, MapVariantMode::Offline),
|
||||||
|
MapVariant::new(MapArea::CraterWest, MapVariantMode::Offline),
|
||||||
|
MapVariant::new(MapArea::CraterSouth, MapVariantMode::Offline),
|
||||||
|
MapVariant::new(MapArea::CraterNorth, MapVariantMode::Offline),
|
||||||
|
MapVariant::new(MapArea::CraterInterior, MapVariantMode::Offline),
|
||||||
|
MapVariant::new(MapArea::SubDesert1, MapVariantMode::Offline),
|
||||||
|
MapVariant::new(MapArea::SubDesert2, MapVariantMode::Offline),
|
||||||
|
MapVariant::new(MapArea::SubDesert3, MapVariantMode::Offline),
|
||||||
|
MapVariant::new(MapArea::SaintMillion, MapVariantMode::Offline),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[error("")]
|
||||||
|
pub enum MapsError {
|
||||||
|
InvalidMonsterId(usize),
|
||||||
|
InvalidObjectId(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Maps {
|
||||||
|
map_variants: Vec<MapVariant>,
|
||||||
|
enemy_data: Vec<Option<MapEnemy>>,
|
||||||
|
object_data: Vec<Option<MapObject>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Maps {
|
||||||
|
pub fn new(map_variants: Vec<MapVariant>, enemy_data: Vec<Option<MapEnemy>>, object_data: Vec<Option<MapObject>>) -> Maps {
|
||||||
Maps {
|
Maps {
|
||||||
enemy_data: map_variants.iter()
|
|
||||||
.flat_map(|map_variant| {
|
|
||||||
enemy_data_from_map_data(map_variant, &room_mode.episode())
|
|
||||||
})
|
|
||||||
.map(|enemy| apply_rare_enemy(enemy, rare_monster_table, event))
|
|
||||||
.collect(),
|
|
||||||
object_data: map_variants.iter()
|
|
||||||
.flat_map(|map_variant| {
|
|
||||||
objects_from_map_data(map_variant.obj_file().into(), &room_mode.episode(), &map_variant.map)
|
|
||||||
}).collect(),
|
|
||||||
map_variants,
|
map_variants,
|
||||||
|
enemy_data,
|
||||||
|
object_data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,7 +329,7 @@ impl Maps {
|
|||||||
{
|
{
|
||||||
self.enemy_data = enemies
|
self.enemy_data = enemies
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|enemy| apply_rare_enemy(enemy, rare_monster_table, event))
|
.map(|enemy| enemy.map(|enemy| rare_monster_table.apply(enemy, event)))
|
||||||
.collect();
|
.collect();
|
||||||
self.object_data = objects;
|
self.object_data = objects;
|
||||||
}
|
}
|
||||||
@ -351,13 +358,20 @@ impl Maps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_rare_enemy(enemy: Option<MapEnemy>, rare_enemy_table: &RareMonsterAppearTable, event: ShipEvent) -> Option<MapEnemy> {
|
pub fn generate_free_roam_maps(room_mode: RoomMode, event: ShipEvent) -> Maps {
|
||||||
enemy.map(|enemy| {
|
let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode());
|
||||||
if enemy.can_be_rare() && rare_enemy_table.roll_is_rare(&enemy.monster) {
|
let map_variants = default_map_variants(room_mode.episode(), room_mode.player_mode());
|
||||||
enemy.into_rare(event)
|
Maps {
|
||||||
}
|
enemy_data: map_variants.iter()
|
||||||
else {
|
.flat_map(|map_variant| {
|
||||||
enemy
|
enemy_data_from_map_data(map_variant, &room_mode.episode())
|
||||||
}
|
})
|
||||||
})
|
.map(|enemy| enemy.map(|enemy| rare_monster_table.apply(enemy, event)))
|
||||||
|
.collect(),
|
||||||
|
object_data: map_variants.iter()
|
||||||
|
.flat_map(|map_variant| {
|
||||||
|
objects_from_map_data(map_variant.obj_file().into(), &room_mode.episode(), &map_variant.map)
|
||||||
|
}).collect(),
|
||||||
|
map_variants,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
pub mod area;
|
pub mod area;
|
||||||
pub mod enemy;
|
pub mod enemy;
|
||||||
mod object;
|
pub mod object;
|
||||||
mod variant;
|
pub mod variant;
|
||||||
mod maps;
|
pub mod maps;
|
||||||
|
|
||||||
// TODO: don't just forward everything to the module scope
|
// TODO: don't just forward everything to the module scope
|
||||||
pub use area::*;
|
pub use area::*;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||||
use crate::common::serverstate::ClientId;
|
use crate::common::serverstate::ClientId;
|
||||||
use crate::ship::ship::{ShipError, Clients, ShipEvent};
|
use crate::ship::ship::{Clients, ShipEvent};
|
||||||
use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError};
|
use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError};
|
||||||
use crate::ship::packet::builder::{player_info};
|
use crate::ship::packet::builder::{player_info};
|
||||||
use crate::ship::items::state::ItemState;
|
use crate::ship::items::state::ItemState;
|
||||||
@ -13,7 +13,7 @@ pub async fn join_lobby(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &ItemState,
|
item_state: &ItemState,
|
||||||
event: ShipEvent)
|
event: ShipEvent)
|
||||||
-> Result<JoinLobby, ShipError> {
|
-> Result<JoinLobby, anyhow::Error> {
|
||||||
let lobby_clients = client_location.get_clients_in_lobby(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
let lobby_clients = client_location.get_clients_in_lobby(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
|
|
||||||
let playerinfo = join_all(
|
let playerinfo = join_all(
|
||||||
@ -28,9 +28,8 @@ pub async fn join_lobby(id: ClientId,
|
|||||||
}}))
|
}}))
|
||||||
.await
|
.await
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Result<Vec<_>, ShipError>>()?;
|
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
||||||
|
|
||||||
//let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
|
||||||
let client_block = clients.with(id, |client| Box::pin(async move {
|
let client_block = clients.with(id, |client| Box::pin(async move {
|
||||||
client.block as u16
|
client.block as u16
|
||||||
})).await?;
|
})).await?;
|
||||||
@ -54,7 +53,7 @@ pub async fn add_to_lobby(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &ItemState,
|
item_state: &ItemState,
|
||||||
event: ShipEvent)
|
event: ShipEvent)
|
||||||
-> Result<AddToLobby, ShipError> {
|
-> Result<AddToLobby, anyhow::Error> {
|
||||||
let area_client = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
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() })?;
|
let leader = client_location.get_lobby_leader(lobby).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
clients.with(id, |client| {
|
clients.with(id, |client| {
|
||||||
@ -77,7 +76,7 @@ pub async fn add_to_lobby(id: ClientId,
|
|||||||
|
|
||||||
pub async fn remove_from_lobby(id: ClientId,
|
pub async fn remove_from_lobby(id: ClientId,
|
||||||
client_location: &ClientLocation)
|
client_location: &ClientLocation)
|
||||||
-> Result<LeaveLobby, ShipError> {
|
-> Result<LeaveLobby, anyhow::Error> {
|
||||||
let prev_area_index = client_location.get_local_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?.local_client.id();
|
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
|
let prev_area_leader_index = client_location
|
||||||
.get_area_leader(client_location
|
.get_area_leader(client_location
|
||||||
|
@ -15,12 +15,11 @@ pub async fn join_room(id: ClientId,
|
|||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
room: &RoomState,
|
room: &RoomState,
|
||||||
event: ShipEvent)
|
event: ShipEvent)
|
||||||
-> Result<JoinRoom, ShipError> {
|
-> Result<JoinRoom, anyhow::Error> {
|
||||||
let all_clients = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
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())
|
let players = futures::stream::iter(all_clients.iter())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.fold::<Result<_, ShipError>, _, _>(Ok([PlayerHeader::default(); 4]), |acc, (i, c)| async move {
|
.fold::<Result<_, anyhow::Error>, _, _>(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()))?;
|
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 {
|
clients.with(c.client, |client| Box::pin(async move {
|
||||||
acc.map(|mut a| {
|
acc.map(|mut a| {
|
||||||
@ -40,14 +39,14 @@ pub async fn join_room(id: ClientId,
|
|||||||
leader: leader.local_client.id(),
|
leader: leader.local_client.id(),
|
||||||
one: 1,
|
one: 1,
|
||||||
difficulty: room.mode.difficulty().into(),
|
difficulty: room.mode.difficulty().into(),
|
||||||
battle: room.mode.battle(),
|
battle: room.mode.battle() as u8,
|
||||||
event: event.into(),
|
event: event.into(),
|
||||||
section: room.section_id.into(),
|
section: room.section_id.into(),
|
||||||
challenge: room.mode.challenge(),
|
challenge: room.mode.challenge() as u8,
|
||||||
random_seed: room.random_seed,
|
random_seed: room.random_seed,
|
||||||
episode: room.mode.episode().into(),
|
episode: room.mode.episode().into(),
|
||||||
one2: 1,
|
one2: 1,
|
||||||
single_player: room.mode.single_player(),
|
single_player: room.mode.player_mode().value(),
|
||||||
unknown: 0,
|
unknown: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -59,7 +58,7 @@ pub async fn add_to_room(_id: ClientId,
|
|||||||
leader: &AreaClient,
|
leader: &AreaClient,
|
||||||
item_state: &ItemState,
|
item_state: &ItemState,
|
||||||
event: ShipEvent)
|
event: ShipEvent)
|
||||||
-> Result<AddToRoom, ShipError> {
|
-> Result<AddToRoom, anyhow::Error> {
|
||||||
let inventory = item_state.get_character_inventory(&client.character).await?;
|
let inventory = item_state.get_character_inventory(&client.character).await?;
|
||||||
Ok(AddToRoom {
|
Ok(AddToRoom {
|
||||||
flag: 1,
|
flag: 1,
|
||||||
|
@ -16,7 +16,7 @@ pub async fn validate_login<EG>(id: ClientId,
|
|||||||
shipgate_sender: &Option<async_std::channel::Sender<ShipMessage>>,
|
shipgate_sender: &Option<async_std::channel::Sender<ShipMessage>>,
|
||||||
ship_name: &str,
|
ship_name: &str,
|
||||||
num_blocks: usize)
|
num_blocks: usize)
|
||||||
-> Result<Vec<SendShipPacket>, ShipError>
|
-> Result<Vec<SendShipPacket>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway,
|
EG: EntityGateway,
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||||
use crate::common::serverstate::ClientId;
|
use crate::common::serverstate::ClientId;
|
||||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
use crate::ship::ship::{SendShipPacket, Clients};
|
||||||
use crate::ship::location::{ClientLocation};
|
use crate::ship::location::{ClientLocation};
|
||||||
use crate::entity::gateway::EntityGateway;
|
use crate::entity::gateway::EntityGateway;
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ pub async fn player_chat(id: ClientId,
|
|||||||
msg: PlayerChat,
|
msg: PlayerChat,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients)
|
clients: &Clients)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let cmsg = clients.with(id, |client| Box::pin(async move {
|
let cmsg = clients.with(id, |client| Box::pin(async move {
|
||||||
PlayerChat::new(client.user.id.0, msg.message)
|
PlayerChat::new(client.user.id.0, msg.message)
|
||||||
})).await?;
|
})).await?;
|
||||||
@ -25,7 +25,7 @@ pub async fn player_chat(id: ClientId,
|
|||||||
pub async fn request_infoboard(id: ClientId,
|
pub async fn request_infoboard(id: ClientId,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients)
|
clients: &Clients)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let area_clients = client_location.get_client_neighbors(id).await.unwrap();
|
let area_clients = client_location.get_client_neighbors(id).await.unwrap();
|
||||||
let infoboards = join_all(
|
let infoboards = join_all(
|
||||||
area_clients.iter()
|
area_clients.iter()
|
||||||
@ -39,7 +39,7 @@ pub async fn request_infoboard(id: ClientId,
|
|||||||
}))
|
}))
|
||||||
.await
|
.await
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Result<Vec<_>, ShipError>>()?;
|
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
||||||
Ok(vec![(id, SendShipPacket::ViewInfoboardResponse(ViewInfoboardResponse {response: infoboards}))])
|
Ok(vec![(id, SendShipPacket::ViewInfoboardResponse(ViewInfoboardResponse {response: infoboards}))])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ pub async fn write_infoboard<EG>(id: ClientId,
|
|||||||
new_infoboard: WriteInfoboard,
|
new_infoboard: WriteInfoboard,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
entity_gateway: &mut EG)
|
entity_gateway: &mut EG)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
|
@ -39,13 +39,15 @@ async fn send_to_client(id: ClientId,
|
|||||||
target: u8,
|
target: u8,
|
||||||
msg: DirectMessage,
|
msg: DirectMessage,
|
||||||
client_location: &ClientLocation)
|
client_location: &ClientLocation)
|
||||||
-> Vec<(ClientId, SendShipPacket)> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
client_location.get_all_clients_by_client(id).await.unwrap().into_iter()
|
Ok(client_location.get_all_clients_by_client(id)
|
||||||
.filter(move |client| client.local_client.id() == target)
|
.await?
|
||||||
.map(move |client| {
|
.into_iter()
|
||||||
(client.client, SendShipPacket::DirectMessage(msg.clone()))
|
.filter(move |client| client.local_client.id() == target)
|
||||||
})
|
.map(move |client| {
|
||||||
.collect()
|
(client.client, SendShipPacket::DirectMessage(msg.clone()))
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn guildcard_send(id: ClientId,
|
pub async fn guildcard_send(id: ClientId,
|
||||||
@ -53,7 +55,7 @@ pub async fn guildcard_send(id: ClientId,
|
|||||||
target: u32,
|
target: u32,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients)
|
clients: &Clients)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let msg = clients.with(id, |client| Box::pin(async move {
|
let msg = clients.with(id, |client| Box::pin(async move {
|
||||||
DirectMessage{
|
DirectMessage{
|
||||||
flag: target,
|
flag: target,
|
||||||
@ -72,7 +74,7 @@ pub async fn guildcard_send(id: ClientId,
|
|||||||
}
|
}
|
||||||
})).await?;
|
})).await?;
|
||||||
|
|
||||||
Ok(send_to_client(id, target as u8, msg, client_location).await)
|
send_to_client(id, target as u8, msg, client_location).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn request_item<EG>(id: ClientId,
|
pub async fn request_item<EG>(id: ClientId,
|
||||||
@ -82,7 +84,7 @@ pub async fn request_item<EG>(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
rooms: &Rooms,
|
rooms: &Rooms,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + 'static,
|
EG: EntityGateway + 'static,
|
||||||
{
|
{
|
||||||
@ -92,7 +94,7 @@ where
|
|||||||
})).await??;
|
})).await??;
|
||||||
|
|
||||||
if monster.dropped_item {
|
if monster.dropped_item {
|
||||||
return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id))
|
return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
let clients_in_area = client_location.get_clients_in_room(room_id).await?;
|
let clients_in_area = client_location.get_clients_in_room(room_id).await?;
|
||||||
@ -115,7 +117,7 @@ where
|
|||||||
z: request_item.z,
|
z: request_item.z,
|
||||||
item: item_drop,
|
item: item_drop,
|
||||||
};
|
};
|
||||||
let character_id = clients.with(id, |client| Box::pin(async move {
|
let character_id = clients.with(area_client.client, |client| Box::pin(async move {
|
||||||
client.character.id
|
client.character.id
|
||||||
})).await?;
|
})).await?;
|
||||||
|
|
||||||
@ -134,7 +136,7 @@ pub async fn pickup_item<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -193,7 +195,7 @@ pub async fn request_box_item<EG>(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
rooms: &Rooms,
|
rooms: &Rooms,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static
|
EG: EntityGateway + Clone + 'static
|
||||||
{
|
{
|
||||||
@ -203,11 +205,10 @@ where
|
|||||||
})).await??;
|
})).await??;
|
||||||
|
|
||||||
if box_object.dropped_item {
|
if box_object.dropped_item {
|
||||||
return Err(ShipError::BoxAlreadyDroppedItem(id, box_drop_request.object_id))
|
return Err(ShipError::BoxAlreadyDroppedItem(id, box_drop_request.object_id).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
let clients_in_area = client_location.get_clients_in_room(room_id).await?;
|
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 {
|
let client_and_drop = rooms.with_mut(room_id, |room| Box::pin(async move {
|
||||||
clients_in_area.into_iter()
|
clients_in_area.into_iter()
|
||||||
.filter_map(move |area_client| {
|
.filter_map(move |area_client| {
|
||||||
@ -244,7 +245,7 @@ where
|
|||||||
pub async fn send_bank_list(id: ClientId,
|
pub async fn send_bank_list(id: ClientId,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
{
|
{
|
||||||
let bank = clients.with(id, |client| {
|
let bank = clients.with(id, |client| {
|
||||||
let item_state = item_state.clone();
|
let item_state = item_state.clone();
|
||||||
@ -262,7 +263,7 @@ pub async fn bank_interaction<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -273,7 +274,7 @@ where
|
|||||||
let mut entity_gateway = entity_gateway.clone();
|
let mut entity_gateway = entity_gateway.clone();
|
||||||
let mut item_state = item_state.clone();
|
let mut item_state = item_state.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
Ok::<_, ShipError>(match bank_interaction.action {
|
Ok::<_, anyhow::Error>(match bank_interaction.action {
|
||||||
BANK_ACTION_DEPOSIT => {
|
BANK_ACTION_DEPOSIT => {
|
||||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||||
deposit_meseta(&mut item_state, &mut entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
|
deposit_meseta(&mut item_state, &mut entity_gateway, &client.character, bank_interaction.meseta_amount).await?;
|
||||||
@ -320,7 +321,7 @@ pub async fn shop_request(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
rooms: &Rooms,
|
rooms: &Rooms,
|
||||||
shops: &ItemShops)
|
shops: &ItemShops)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
{
|
{
|
||||||
//let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
//let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
let room_id = client_location.get_room(id).await?;
|
let room_id = client_location.get_room(id).await?;
|
||||||
@ -397,7 +398,7 @@ pub async fn buy_item<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -422,7 +423,7 @@ where
|
|||||||
(item, remove)
|
(item, remove)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShipError::ShopError)
|
return Err(ShipError::ShopError.into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -439,7 +440,7 @@ where
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)
|
Ok::<_, anyhow::Error>(builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)?)
|
||||||
})}).await??;
|
})}).await??;
|
||||||
|
|
||||||
let other_clients_in_area = client_location.get_client_neighbors(id).await?;
|
let other_clients_in_area = client_location.get_client_neighbors(id).await?;
|
||||||
@ -465,7 +466,7 @@ pub async fn request_tek_item<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -502,7 +503,7 @@ where
|
|||||||
});
|
});
|
||||||
|
|
||||||
take_meseta(&mut 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)
|
Ok::<_, anyhow::Error>(builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?)
|
||||||
})}).await??;
|
})}).await??;
|
||||||
|
|
||||||
|
|
||||||
@ -515,7 +516,7 @@ pub async fn accept_tek_item<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
|
@ -16,7 +16,7 @@ pub async fn block_selected(id: ClientId,
|
|||||||
pkt: MenuSelect,
|
pkt: MenuSelect,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &ItemState)
|
item_state: &ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
clients.with_mut(id, |client| {
|
clients.with_mut(id, |client| {
|
||||||
let item_state = item_state.clone();
|
let item_state = item_state.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -34,8 +34,8 @@ pub async fn block_selected(id: ClientId,
|
|||||||
.meseta(inventory.meseta)
|
.meseta(inventory.meseta)
|
||||||
.inventory(&inventory)
|
.inventory(&inventory)
|
||||||
.bank(&bank)
|
.bank(&bank)
|
||||||
.keyboard_config(&client.character.keyboard_config.as_bytes())
|
.keyboard_config(&client.settings.settings.keyboard_config)
|
||||||
.gamepad_config(&client.character.gamepad_config.as_bytes())
|
.gamepad_config(&client.settings.settings.gamepad_config)
|
||||||
.symbol_chat(&client.settings.settings.symbol_chats)
|
.symbol_chat(&client.settings.settings.symbol_chats)
|
||||||
.tech_menu(&client.character.tech_menu.as_bytes())
|
.tech_menu(&client.character.tech_menu.as_bytes())
|
||||||
.option_flags(client.character.option_flags)
|
.option_flags(client.character.option_flags)
|
||||||
@ -57,7 +57,7 @@ pub async fn send_player_to_lobby(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &ItemState,
|
item_state: &ItemState,
|
||||||
event: ShipEvent)
|
event: ShipEvent)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).await.map_err(|_| ShipError::TooManyClients)?;
|
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, event).await?;
|
let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, event).await?;
|
||||||
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?;
|
let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, event).await?;
|
||||||
@ -77,7 +77,7 @@ pub async fn change_lobby<EG>(id: ClientId,
|
|||||||
rooms: &Rooms,
|
rooms: &Rooms,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
event: ShipEvent)
|
event: ShipEvent)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -134,7 +134,7 @@ where
|
|||||||
|
|
||||||
pub async fn remove_from_lobby(id: ClientId,
|
pub async fn remove_from_lobby(id: ClientId,
|
||||||
client_location: &mut ClientLocation)
|
client_location: &mut ClientLocation)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let area_client = client_location.get_local_client(id).await?;
|
let area_client = client_location.get_local_client(id).await?;
|
||||||
let neighbors = client_location.get_client_neighbors(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 leader = client_location.get_leader_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
@ -150,7 +150,7 @@ pub async fn get_room_tab_info(id: ClientId,
|
|||||||
pkt: MenuDetail,
|
pkt: MenuDetail,
|
||||||
client_location: &mut ClientLocation,
|
client_location: &mut ClientLocation,
|
||||||
clients: &Clients)
|
clients: &Clients)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let room_id = RoomId(pkt.item as usize);
|
let room_id = RoomId(pkt.item as usize);
|
||||||
let clients_in_room = client_location.get_clients_in_room(room_id).await.map_err(|err| -> ClientLocationError { err.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() {
|
let room_info = if clients_in_room.is_empty() {
|
||||||
@ -169,7 +169,7 @@ pub async fn get_room_tab_info(id: ClientId,
|
|||||||
})).await
|
})).await
|
||||||
})).await
|
})).await
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Result<Vec<_>, ShipError>>()?
|
.collect::<Result<Vec<_>, anyhow::Error>>()?
|
||||||
.join("\n")
|
.join("\n")
|
||||||
};
|
};
|
||||||
Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new(room_info)))])
|
Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new(room_info)))])
|
||||||
|
@ -18,7 +18,7 @@ pub async fn request_exp<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
rooms: &Rooms)
|
rooms: &Rooms)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -29,7 +29,7 @@ where
|
|||||||
let enemy_exp = rooms.with(room_id, |room| Box::pin(async move {
|
let enemy_exp = rooms.with(room_id, |room| Box::pin(async move {
|
||||||
let monster = room.maps.enemy_by_id(enemy_id)?;
|
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))?;
|
let monster_stats = room.monster_stats.get(&monster.monster).ok_or_else(|| ShipError::UnknownMonster(monster.monster))?;
|
||||||
Ok::<_, ShipError>(monster_stats.exp)
|
Ok::<_, anyhow::Error>(monster_stats.exp)
|
||||||
})).await??;
|
})).await??;
|
||||||
|
|
||||||
let exp_gain = if request_exp.last_hitter == 1 {
|
let exp_gain = if request_exp.last_hitter == 1 {
|
||||||
@ -59,7 +59,7 @@ where
|
|||||||
let (_, before_stats) = LEVEL_TABLE.get_stats_from_exp(char_class, exp);
|
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 (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);
|
let level_up_pkt = builder::message::character_leveled_up(area_client, after_level-1, before_stats, after_stats);
|
||||||
exp_pkts.extend(clients_in_area.into_iter()
|
exp_pkts.extend(clients_in_area.into_iter()
|
||||||
.map(move |c| {
|
.map(move |c| {
|
||||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerLevelUp(level_up_pkt.clone()))))
|
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerLevelUp(level_up_pkt.clone()))))
|
||||||
@ -83,7 +83,7 @@ pub async fn player_drop_item<EG>(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
rooms: &Rooms,
|
rooms: &Rooms,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -112,7 +112,7 @@ pub async fn drop_coordinates(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
rooms: &Rooms)
|
rooms: &Rooms)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
{
|
{
|
||||||
let room_id = client_location.get_room(id).await.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 {
|
let map_area = rooms.with(room_id, |room| Box::pin(async move {
|
||||||
@ -137,7 +137,7 @@ pub async fn no_longer_has_item<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -150,7 +150,7 @@ where
|
|||||||
})).await?;
|
})).await?;
|
||||||
if let Some(drop_location) = drop_location {
|
if let Some(drop_location) = drop_location {
|
||||||
if drop_location.item_id.0 != no_longer_has_item.item_id {
|
if drop_location.item_id.0 != no_longer_has_item.item_id {
|
||||||
return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id));
|
return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if no_longer_has_item.item_id == 0xFFFFFFFF {
|
if no_longer_has_item.item_id == 0xFFFFFFFF {
|
||||||
@ -218,7 +218,7 @@ where
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Err(ShipError::InvalidItem(ClientItemId(no_longer_has_item.item_id)))
|
Err(ShipError::InvalidItem(ClientItemId(no_longer_has_item.item_id)).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +227,7 @@ pub async fn update_player_position(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
rooms: &Rooms)
|
rooms: &Rooms)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
if let Ok(room_id) = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() }) {
|
if let Ok(room_id) = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() }) {
|
||||||
let msg = message.msg.clone();
|
let msg = message.msg.clone();
|
||||||
clients.with_mut(id, |client| {
|
clients.with_mut(id, |client| {
|
||||||
@ -291,7 +291,7 @@ pub async fn update_player_position(id: ClientId,
|
|||||||
}
|
}
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
Ok::<_, ShipError>(())
|
Ok::<_, anyhow::Error>(())
|
||||||
})}).await??;
|
})}).await??;
|
||||||
}
|
}
|
||||||
Ok(client_location.get_client_neighbors(id).await?.into_iter()
|
Ok(client_location.get_client_neighbors(id).await?.into_iter()
|
||||||
@ -307,7 +307,7 @@ pub async fn charge_attack<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -333,7 +333,7 @@ pub async fn player_uses_item<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -364,7 +364,7 @@ pub async fn player_used_medical_center<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -390,7 +390,7 @@ pub async fn player_feed_mag<EG>(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -415,7 +415,7 @@ pub async fn player_equips_item<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -440,7 +440,7 @@ pub async fn player_unequips_item<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -458,7 +458,7 @@ pub async fn player_sorts_items<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -488,7 +488,7 @@ pub async fn player_sells_item<EG> (id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState)
|
item_state: &mut ItemState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,8 @@ use libpso::packet::ship::*;
|
|||||||
use crate::common::serverstate::ClientId;
|
use crate::common::serverstate::ClientId;
|
||||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients, ShipEvent};
|
use crate::ship::ship::{SendShipPacket, ShipError, Clients, ShipEvent};
|
||||||
use crate::ship::room::Rooms;
|
use crate::ship::room::Rooms;
|
||||||
use crate::ship::location::{ClientLocation, ClientLocationError};
|
use crate::ship::map::enemy::RareMonsterAppearTable;
|
||||||
|
use crate::ship::location::{ClientLocation};
|
||||||
use crate::ship::packet::builder::quest;
|
use crate::ship::packet::builder::quest;
|
||||||
use libpso::util::array_to_utf8;
|
use libpso::util::array_to_utf8;
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ enum QuestFileType {
|
|||||||
Dat
|
Dat
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_filename(filename_bytes: &[u8; 16]) -> Result<(u16, u16, QuestFileType), ShipError> {
|
fn parse_filename(filename_bytes: &[u8; 16]) -> Result<(u16, u16, QuestFileType), anyhow::Error> {
|
||||||
let filename = array_to_utf8(*filename_bytes).map_err(|_| ShipError::InvalidQuestFilename("NOT UTF8".to_string()))?;
|
let filename = array_to_utf8(*filename_bytes).map_err(|_| ShipError::InvalidQuestFilename("NOT UTF8".to_string()))?;
|
||||||
let (filename, suffix) = {
|
let (filename, suffix) = {
|
||||||
let mut s = filename.splitn(2, '.');
|
let mut s = filename.splitn(2, '.');
|
||||||
@ -24,7 +25,7 @@ fn parse_filename(filename_bytes: &[u8; 16]) -> Result<(u16, u16, QuestFileType)
|
|||||||
let datatype = match suffix {
|
let datatype = match suffix {
|
||||||
"bin" => QuestFileType::Bin,
|
"bin" => QuestFileType::Bin,
|
||||||
"dat" => QuestFileType::Dat,
|
"dat" => QuestFileType::Dat,
|
||||||
_ => return Err(ShipError::InvalidQuestFilename(filename.to_owned()))
|
_ => Err(ShipError::InvalidQuestFilename(filename.to_owned()))?
|
||||||
};
|
};
|
||||||
|
|
||||||
let (category, quest) = {
|
let (category, quest) = {
|
||||||
@ -41,8 +42,8 @@ pub async fn send_quest_category_list(id: ClientId,
|
|||||||
rql: RequestQuestList,
|
rql: RequestQuestList,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
rooms: &Rooms)
|
rooms: &Rooms)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
let room_id = client_location.get_room(id).await?;
|
||||||
let rql = rql.clone();
|
let rql = rql.clone();
|
||||||
rooms.with_mut(room_id, |room| Box::pin(async move {
|
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]);
|
let qcl = quest::quest_category_list(&room.quests[rql.flag.clamp(0, (room.quests.len() - 1) as u32) as usize]);
|
||||||
@ -55,8 +56,8 @@ pub async fn select_quest_category(id: ClientId,
|
|||||||
menuselect: MenuSelect,
|
menuselect: MenuSelect,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
rooms: &Rooms)
|
rooms: &Rooms)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
let room_id = client_location.get_room(id).await?;
|
||||||
rooms.with(room_id, |room| Box::pin(async move {
|
rooms.with(room_id, |room| Box::pin(async move {
|
||||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||||
.nth(menuselect.item as usize)
|
.nth(menuselect.item as usize)
|
||||||
@ -72,8 +73,8 @@ pub async fn quest_detail(id: ClientId,
|
|||||||
questdetailrequest: QuestDetailRequest,
|
questdetailrequest: QuestDetailRequest,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
rooms: &Rooms)
|
rooms: &Rooms)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
let room_id = client_location.get_room(id).await?;
|
||||||
rooms.with(room_id, |room| Box::pin(async move {
|
rooms.with(room_id, |room| Box::pin(async move {
|
||||||
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
let (_, category_quests) = room.quests[room.quest_group.value()].iter()
|
||||||
.nth(questdetailrequest.category as usize)
|
.nth(questdetailrequest.category as usize)
|
||||||
@ -96,8 +97,8 @@ pub async fn player_chose_quest(id: ClientId,
|
|||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
rooms: &Rooms,
|
rooms: &Rooms,
|
||||||
event: ShipEvent)
|
event: ShipEvent)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
let room_id = client_location.get_room(id).await?;
|
||||||
|
|
||||||
let client_location = client_location.clone();
|
let client_location = client_location.clone();
|
||||||
let questmenuselect = questmenuselect.clone();
|
let questmenuselect = questmenuselect.clone();
|
||||||
@ -115,14 +116,14 @@ pub async fn player_chose_quest(id: ClientId,
|
|||||||
.ok_or_else(|| ShipError::InvalidQuest(questmenuselect.quest))?
|
.ok_or_else(|| ShipError::InvalidQuest(questmenuselect.quest))?
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let rare_monster_drops = room.rare_monster_table.clone();
|
let rare_monster_table = RareMonsterAppearTable::new(room.mode.episode());
|
||||||
room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &rare_monster_drops, event);
|
room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &rare_monster_table, event);
|
||||||
room.map_areas = quest.map_areas.clone();
|
room.map_areas = quest.map_areas.clone();
|
||||||
|
|
||||||
let bin = quest::quest_header(&questmenuselect, &quest.bin_blob, "bin");
|
let bin = quest::quest_header(&questmenuselect, &quest.bin_blob, "bin");
|
||||||
let dat = quest::quest_header(&questmenuselect, &quest.dat_blob, "dat");
|
let dat = quest::quest_header(&questmenuselect, &quest.dat_blob, "dat");
|
||||||
|
|
||||||
let area_clients = client_location.get_all_clients_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
let area_clients = client_location.get_all_clients_by_client(id).await?;
|
||||||
for client in &area_clients {
|
for client in &area_clients {
|
||||||
clients.with_mut(client.client, |client| Box::pin(async move {
|
clients.with_mut(client.client, |client| Box::pin(async move {
|
||||||
client.done_loading_quest = false;
|
client.done_loading_quest = false;
|
||||||
@ -141,9 +142,9 @@ pub async fn quest_file_request(id: ClientId,
|
|||||||
quest_file_request: QuestFileRequest,
|
quest_file_request: QuestFileRequest,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
rooms: &mut Rooms)
|
rooms: &mut Rooms)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
{
|
{
|
||||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
let room_id = client_location.get_room(id).await?;
|
||||||
|
|
||||||
let quest_file_request = quest_file_request.clone();
|
let quest_file_request = quest_file_request.clone();
|
||||||
rooms.with(room_id, |room| Box::pin(async move {
|
rooms.with(room_id, |room| Box::pin(async move {
|
||||||
@ -175,8 +176,8 @@ pub async fn quest_chunk_ack(id: ClientId,
|
|||||||
quest_chunk_ack: QuestChunkAck,
|
quest_chunk_ack: QuestChunkAck,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
rooms: &Rooms)
|
rooms: &Rooms)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
let room_id = client_location.get_room(id).await?;
|
||||||
|
|
||||||
let quest_chunk_ack = quest_chunk_ack.clone();
|
let quest_chunk_ack = quest_chunk_ack.clone();
|
||||||
rooms.with(room_id, |room| Box::pin(async move {
|
rooms.with(room_id, |room| Box::pin(async move {
|
||||||
@ -211,11 +212,11 @@ pub async fn quest_chunk_ack(id: ClientId,
|
|||||||
pub async fn done_loading_quest(id: ClientId,
|
pub async fn done_loading_quest(id: ClientId,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
client_location: &ClientLocation)
|
client_location: &ClientLocation)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
clients.with_mut(id, |client| Box::pin(async move {
|
clients.with_mut(id, |client| Box::pin(async move {
|
||||||
client.done_loading_quest = true;
|
client.done_loading_quest = true;
|
||||||
})).await?;
|
})).await?;
|
||||||
let area_clients = client_location.get_all_clients_by_client(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
let area_clients = client_location.get_all_clients_by_client(id).await?;
|
||||||
|
|
||||||
let all_loaded = area_clients.iter()
|
let all_loaded = area_clients.iter()
|
||||||
.map(|client|
|
.map(|client|
|
||||||
|
@ -1,36 +1,43 @@
|
|||||||
use std::convert::{TryFrom, Into};
|
use std::convert::{TryFrom, Into};
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
|
||||||
|
use async_std::sync::Arc;
|
||||||
|
|
||||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||||
use libpso::packet::messages::*;
|
use libpso::packet::messages::*;
|
||||||
use crate::common::serverstate::ClientId;
|
use crate::common::serverstate::ClientId;
|
||||||
use crate::common::leveltable::LEVEL_TABLE;
|
use crate::common::leveltable::LEVEL_TABLE;
|
||||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients, ShipEvent};
|
use crate::entity::character::SectionID;
|
||||||
use crate::ship::room::Rooms;
|
use crate::ship::drops::DropTable;
|
||||||
|
use crate::ship::ship::{SendShipPacket, Clients, ShipEvent};
|
||||||
|
use crate::ship::room::{Rooms, Episode, Difficulty, RoomState, RoomMode};
|
||||||
|
use crate::ship::map::Maps;
|
||||||
use crate::ship::location::{ClientLocation, RoomId, RoomLobby, GetAreaError};
|
use crate::ship::location::{ClientLocation, RoomId, RoomLobby, GetAreaError};
|
||||||
use crate::ship::packet::builder;
|
use crate::ship::packet::builder;
|
||||||
use crate::ship::room;
|
|
||||||
use crate::ship::items::state::ItemState;
|
use crate::ship::items::state::ItemState;
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn create_room(id: ClientId,
|
pub async fn create_room(id: ClientId,
|
||||||
create_room: CreateRoom,
|
create_room: CreateRoom,
|
||||||
client_location: &mut ClientLocation,
|
client_location: &mut ClientLocation,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState,
|
item_state: &mut ItemState,
|
||||||
rooms: &Rooms,
|
rooms: &Rooms,
|
||||||
|
map_builder: Arc<Box<dyn Fn(RoomMode, ShipEvent) -> Maps + Send + Sync>>,
|
||||||
|
drop_table_builder: Arc<Box<dyn Fn(Episode, Difficulty, SectionID) -> DropTable + Send + Sync>>,
|
||||||
event: ShipEvent)
|
event: ShipEvent)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let level = clients.with(id, |client| Box::pin(async move {
|
let level = clients.with(id, |client| Box::pin(async move {
|
||||||
LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp)
|
LEVEL_TABLE.get_level_from_exp(client.character.char_class, client.character.exp)
|
||||||
})).await?;
|
})).await?;
|
||||||
match room::Difficulty::try_from(create_room.difficulty)? {
|
match Difficulty::try_from(create_room.difficulty)? {
|
||||||
room::Difficulty::Ultimate if level < 80 => {
|
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())))])
|
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 => {
|
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())))])
|
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 => {
|
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())))])
|
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 20 \nto create Hard rooms.".into())))])
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@ -45,9 +52,9 @@ pub async fn create_room(id: ClientId,
|
|||||||
let mut item_state = item_state.clone();
|
let mut item_state = item_state.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
item_state.add_character_to_room(room_id, &client.character, area_client).await;
|
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, event)?;
|
let mut room = RoomState::from_create_room(&create_room, map_builder, drop_table_builder, client.character.section_id, event)?;
|
||||||
room.bursting = true;
|
room.bursting = true;
|
||||||
Ok::<_, ShipError>(room)
|
Ok::<_, anyhow::Error>(room)
|
||||||
})}).await??;
|
})}).await??;
|
||||||
|
|
||||||
let join_room = builder::room::join_room(id, clients, client_location, room_id, &room, event).await?;
|
let join_room = builder::room::join_room(id, clients, client_location, room_id, &room, event).await?;
|
||||||
@ -69,7 +76,7 @@ pub async fn create_room(id: ClientId,
|
|||||||
pub async fn room_name_request(id: ClientId,
|
pub async fn room_name_request(id: ClientId,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
rooms: &Rooms)
|
rooms: &Rooms)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let area = client_location.get_area(id).await?;
|
let area = client_location.get_area(id).await?;
|
||||||
match area {
|
match area {
|
||||||
RoomLobby::Room(room) => {
|
RoomLobby::Room(room) => {
|
||||||
@ -90,7 +97,7 @@ pub async fn join_room(id: ClientId,
|
|||||||
item_state: &mut ItemState,
|
item_state: &mut ItemState,
|
||||||
rooms: &Rooms,
|
rooms: &Rooms,
|
||||||
event: ShipEvent)
|
event: ShipEvent)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let room_id = RoomId(pkt.item as usize);
|
let room_id = RoomId(pkt.item as usize);
|
||||||
if !rooms.exists(room_id).await {
|
if !rooms.exists(room_id).await {
|
||||||
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("This room no longer exists!".into())))])
|
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("This room no longer exists!".into())))])
|
||||||
@ -103,13 +110,13 @@ pub async fn join_room(id: ClientId,
|
|||||||
})).await?;
|
})).await?;
|
||||||
|
|
||||||
match difficulty {
|
match difficulty {
|
||||||
room::Difficulty::Ultimate if level < 80 => {
|
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())))])
|
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 => {
|
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())))])
|
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 => {
|
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())))])
|
return Ok(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("You must be at least level 20 \nto join Hard rooms.".into())))])
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@ -171,7 +178,7 @@ pub async fn join_room(id: ClientId,
|
|||||||
pub async fn done_bursting(id: ClientId,
|
pub async fn done_bursting(id: ClientId,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
rooms: &Rooms)
|
rooms: &Rooms)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let room_id = client_location.get_room(id).await?;
|
let room_id = client_location.get_room(id).await?;
|
||||||
let rare_monster_list = rooms.with_mut(room_id, |room| Box::pin(async move {
|
let rare_monster_list = rooms.with_mut(room_id, |room| Box::pin(async move {
|
||||||
room.bursting = false;
|
room.bursting = false;
|
||||||
@ -235,7 +242,7 @@ pub async fn request_room_list(id: ClientId,
|
|||||||
pub async fn cool_62(id: ClientId,
|
pub async fn cool_62(id: ClientId,
|
||||||
cool_62: Like62ButCooler,
|
cool_62: Like62ButCooler,
|
||||||
client_location: &ClientLocation)
|
client_location: &ClientLocation)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError> {
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error> {
|
||||||
let target = cool_62.flag as u8;
|
let target = cool_62.flag as u8;
|
||||||
let cool_62 = cool_62.clone();
|
let cool_62 = cool_62.clone();
|
||||||
Ok(client_location
|
Ok(client_location
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||||
use crate::common::serverstate::ClientId;
|
use crate::common::serverstate::ClientId;
|
||||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
use crate::ship::ship::{SendShipPacket, Clients};
|
||||||
use crate::entity::gateway::EntityGateway;
|
use crate::entity::gateway::EntityGateway;
|
||||||
|
|
||||||
pub async fn update_config<EG>(id: ClientId,
|
pub async fn update_config<EG>(id: ClientId,
|
||||||
update_config: UpdateConfig,
|
update_config: UpdateConfig,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
entity_gateway: &mut EG)
|
entity_gateway: &mut EG)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -24,7 +24,7 @@ pub async fn save_options<EG>(id: ClientId,
|
|||||||
save_options: SaveOptions,
|
save_options: SaveOptions,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
entity_gateway: &mut EG)
|
entity_gateway: &mut EG)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -41,15 +41,15 @@ pub async fn keyboard_config<EG>(id: ClientId,
|
|||||||
keyboard_config: KeyboardConfig,
|
keyboard_config: KeyboardConfig,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
entity_gateway: &mut EG)
|
entity_gateway: &mut EG)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
clients.with_mut(id, |client| {
|
clients.with_mut(id, |client| {
|
||||||
let mut entity_gateway = entity_gateway.clone();
|
let mut entity_gateway = entity_gateway.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
client.character.keyboard_config.update(&keyboard_config);
|
client.settings.settings.keyboard_config = keyboard_config.keyboard_config;
|
||||||
entity_gateway.save_character(&client.character).await
|
entity_gateway.save_user_settings(&client.settings).await
|
||||||
})}).await??;
|
})}).await??;
|
||||||
Ok(Vec::new())
|
Ok(Vec::new())
|
||||||
}
|
}
|
||||||
@ -58,15 +58,15 @@ pub async fn gamepad_config<EG>(id: ClientId,
|
|||||||
gamepad_config: GamepadConfig,
|
gamepad_config: GamepadConfig,
|
||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
entity_gateway: &mut EG)
|
entity_gateway: &mut EG)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
clients.with_mut(id, |client| {
|
clients.with_mut(id, |client| {
|
||||||
let mut entity_gateway = entity_gateway.clone();
|
let mut entity_gateway = entity_gateway.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
client.character.gamepad_config.update(&gamepad_config);
|
client.settings.settings.gamepad_config = gamepad_config.gamepad_config;
|
||||||
entity_gateway.save_character(&client.character).await
|
entity_gateway.save_user_settings(&client.settings).await
|
||||||
})}).await??;
|
})}).await??;
|
||||||
Ok(Vec::new())
|
Ok(Vec::new())
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use libpso::packet::ship::*;
|
|||||||
use libpso::packet::messages::*;
|
use libpso::packet::messages::*;
|
||||||
use crate::common::serverstate::ClientId;
|
use crate::common::serverstate::ClientId;
|
||||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
||||||
use crate::ship::location::{ClientLocation, ClientLocationError};
|
use crate::ship::location::{ClientLocation};
|
||||||
use crate::ship::items::ClientItemId;
|
use crate::ship::items::ClientItemId;
|
||||||
use crate::ship::items::state::{ItemState, ItemStateError};
|
use crate::ship::items::state::{ItemState, ItemStateError};
|
||||||
use crate::ship::items::inventory::InventoryItemDetail;
|
use crate::ship::items::inventory::InventoryItemDetail;
|
||||||
@ -57,9 +57,9 @@ async fn do_trade_action<F>(id: ClientId,
|
|||||||
this: &mut ClientTradeState,
|
this: &mut ClientTradeState,
|
||||||
other: &mut ClientTradeState,
|
other: &mut ClientTradeState,
|
||||||
action: F)
|
action: F)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> Result<(), ShipError>,
|
F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> Result<(), anyhow::Error>,
|
||||||
{
|
{
|
||||||
Ok(match action(this, other) {
|
Ok(match action(this, other) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
@ -92,7 +92,7 @@ pub async fn trade_request(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState,
|
item_state: &mut ItemState,
|
||||||
trades: &mut TradeState)
|
trades: &mut TradeState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
{
|
{
|
||||||
let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet
|
let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet
|
||||||
match trade_request.trade {
|
match trade_request.trade {
|
||||||
@ -293,12 +293,12 @@ async fn inner_items_to_trade(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState,
|
item_state: &mut ItemState,
|
||||||
trades: &mut TradeState)
|
trades: &mut TradeState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
{
|
{
|
||||||
let pkts = trades
|
let pkts = trades
|
||||||
.with(&id, |mut this, other| async move {
|
.with(&id, |mut this, other| async move {
|
||||||
if status_is_not(&this.status, &[TradeStatus::FinalConfirm]) || status_is_not(&other.status, &[TradeStatus::FinalConfirm, TradeStatus::ItemsChecked]) {
|
if status_is_not(&this.status, &[TradeStatus::FinalConfirm]) || status_is_not(&other.status, &[TradeStatus::FinalConfirm, TradeStatus::ItemsChecked]) {
|
||||||
return Err(ShipError::from(TradeError::MismatchedStatus))
|
return Err(anyhow::Error::from(ShipError::from(TradeError::MismatchedStatus)))
|
||||||
}
|
}
|
||||||
let other_client = other.client();
|
let other_client = other.client();
|
||||||
let (this_inventory, other_inventory) = clients.with(this.client(), |client| {
|
let (this_inventory, other_inventory) = clients.with(this.client(), |client| {
|
||||||
@ -311,7 +311,7 @@ async fn inner_items_to_trade(id: ClientId,
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
item_state.get_character_inventory(&client.character).await
|
item_state.get_character_inventory(&client.character).await
|
||||||
})}).await??;
|
})}).await??;
|
||||||
Ok::<_, ShipError>((this, other_inventory))
|
Ok::<_, anyhow::Error>((this, other_inventory))
|
||||||
})}).await??;
|
})}).await??;
|
||||||
|
|
||||||
if items_to_trade.count as usize != (this.items.len() + usize::from(this.meseta != 0)) {
|
if items_to_trade.count as usize != (this.items.len() + usize::from(this.meseta != 0)) {
|
||||||
@ -383,7 +383,7 @@ async fn inner_items_to_trade(id: ClientId,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, ShipError>>()?;
|
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
||||||
|
|
||||||
this.status = TradeStatus::ItemsChecked;
|
this.status = TradeStatus::ItemsChecked;
|
||||||
if this.status == TradeStatus::ItemsChecked && other.status == TradeStatus::ItemsChecked {
|
if this.status == TradeStatus::ItemsChecked && other.status == TradeStatus::ItemsChecked {
|
||||||
@ -418,7 +418,7 @@ pub async fn items_to_trade(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState,
|
item_state: &mut ItemState,
|
||||||
trades: &mut TradeState)
|
trades: &mut TradeState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
{
|
{
|
||||||
let t = inner_items_to_trade(id, items_to_trade_pkt, client_location, clients, item_state, trades).await;
|
let t = inner_items_to_trade(id, items_to_trade_pkt, client_location, clients, item_state, trades).await;
|
||||||
match t {
|
match t {
|
||||||
@ -443,7 +443,7 @@ async fn trade_confirmed_inner<EG>(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState,
|
item_state: &mut ItemState,
|
||||||
trades: &mut TradeState)
|
trades: &mut TradeState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
@ -460,14 +460,14 @@ where
|
|||||||
.with(&id, |mut this, other| {
|
.with(&id, |mut this, other| {
|
||||||
async move {
|
async move {
|
||||||
if status_is_not(&this.status, &[TradeStatus::ItemsChecked]) || status_is_not(&other.status, &[TradeStatus::ItemsChecked, TradeStatus::TradeComplete]) {
|
if status_is_not(&this.status, &[TradeStatus::ItemsChecked]) || status_is_not(&other.status, &[TradeStatus::ItemsChecked, TradeStatus::TradeComplete]) {
|
||||||
return Err(ShipError::TradeError(TradeError::MismatchedStatus))
|
return Err(anyhow::Error::from(ShipError::TradeError(TradeError::MismatchedStatus)))
|
||||||
}
|
}
|
||||||
this.status = TradeStatus::TradeComplete;
|
this.status = TradeStatus::TradeComplete;
|
||||||
|
|
||||||
if this.status == TradeStatus::TradeComplete && other.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 this_local_client = client_location.get_local_client(this.client()).await?;
|
||||||
let other_local_client = client_location.get_local_client(other.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() })?;
|
let room_id = client_location.get_room(id).await?;
|
||||||
|
|
||||||
Ok(TradeReady::BothPlayers(room_id,
|
Ok(TradeReady::BothPlayers(room_id,
|
||||||
(this_local_client, /*this_client, */this.clone()),
|
(this_local_client, /*this_client, */this.clone()),
|
||||||
@ -584,7 +584,7 @@ pub async fn trade_confirmed<EG>(id: ClientId,
|
|||||||
clients: &Clients,
|
clients: &Clients,
|
||||||
item_state: &mut ItemState,
|
item_state: &mut ItemState,
|
||||||
trades: &mut TradeState)
|
trades: &mut TradeState)
|
||||||
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
|
-> Result<Vec<(ClientId, SendShipPacket)>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway + Clone + 'static,
|
EG: EntityGateway + Clone + 'static,
|
||||||
{
|
{
|
||||||
|
@ -13,12 +13,10 @@ use crate::ship::drops::DropTable;
|
|||||||
use crate::entity::character::SectionID;
|
use crate::entity::character::SectionID;
|
||||||
use crate::ship::monster::{load_monster_stats_table, MonsterType, MonsterStats};
|
use crate::ship::monster::{load_monster_stats_table, MonsterType, MonsterStats};
|
||||||
use crate::ship::map::area::MapAreaLookup;
|
use crate::ship::map::area::MapAreaLookup;
|
||||||
use crate::ship::map::enemy::RareMonsterAppearTable;
|
|
||||||
use crate::ship::quests;
|
use crate::ship::quests;
|
||||||
use crate::ship::ship::{ShipError, ShipEvent};
|
use crate::ship::ship::{ShipError, ShipEvent};
|
||||||
use crate::ship::location::{MAX_ROOMS, RoomId};
|
use crate::ship::location::{MAX_ROOMS, RoomId};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Rooms([Arc<RwLock<Option<RoomState>>>; MAX_ROOMS]);
|
pub struct Rooms([Arc<RwLock<Option<RoomState>>>; MAX_ROOMS]);
|
||||||
|
|
||||||
@ -29,7 +27,7 @@ impl Default for Rooms {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Rooms {
|
impl Rooms {
|
||||||
pub async fn add(&self, room_id: RoomId, room: RoomState) -> Result<(), ShipError> {
|
pub async fn add(&self, room_id: RoomId, room: RoomState) -> Result<(), anyhow::Error> {
|
||||||
*self.0
|
*self.0
|
||||||
.get(room_id.0)
|
.get(room_id.0)
|
||||||
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
|
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
|
||||||
@ -58,7 +56,7 @@ impl Rooms {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn with<'a, T, F>(&'a self, room_id: RoomId, func: F) -> Result<T, ShipError>
|
pub async fn with<'a, T, F>(&'a self, room_id: RoomId, func: F) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
T: Send,
|
T: Send,
|
||||||
F: for<'b> FnOnce(&'b RoomState) -> BoxFuture<'b, T> + Send + 'a
|
F: for<'b> FnOnce(&'b RoomState) -> BoxFuture<'b, T> + Send + 'a
|
||||||
@ -72,11 +70,11 @@ impl Rooms {
|
|||||||
Ok(func(room).await)
|
Ok(func(room).await)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Err(ShipError::InvalidRoom(room_id.0 as u32))
|
Err(ShipError::InvalidRoom(room_id.0 as u32).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn with_mut<'a, T, F>(&'a self, room_id: RoomId, func: F) -> Result<T, ShipError>
|
pub async fn with_mut<'a, T, F>(&'a self, room_id: RoomId, func: F) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
T: Send,
|
T: Send,
|
||||||
F: for<'b> FnOnce(&'b mut RoomState) -> BoxFuture<'b, T> + Send + 'a
|
F: for<'b> FnOnce(&'b mut RoomState) -> BoxFuture<'b, T> + Send + 'a
|
||||||
@ -91,7 +89,7 @@ impl Rooms {
|
|||||||
Ok(func(room).await)
|
Ok(func(room).await)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Err(ShipError::InvalidRoom(room_id.0 as u32))
|
Err(ShipError::InvalidRoom(room_id.0 as u32).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +133,21 @@ pub enum Episode {
|
|||||||
Four,
|
Four,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum PlayerMode{
|
||||||
|
Single,
|
||||||
|
Multi,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerMode {
|
||||||
|
pub fn value(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
PlayerMode::Single => 1,
|
||||||
|
PlayerMode::Multi => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<u8> for Episode {
|
impl TryFrom<u8> for Episode {
|
||||||
type Error = RoomCreationError;
|
type Error = RoomCreationError;
|
||||||
|
|
||||||
@ -245,24 +258,18 @@ impl RoomMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn battle(&self) -> u8 {
|
pub fn battle(&self) -> bool {
|
||||||
match self {
|
matches!(self, RoomMode::Battle {..})
|
||||||
RoomMode::Battle {..} => 1,
|
|
||||||
_ => 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn challenge(&self) -> u8 {
|
pub fn challenge(&self) -> bool {
|
||||||
match self {
|
matches!(self, RoomMode::Challenge {..})
|
||||||
RoomMode::Challenge {..} => 1,
|
|
||||||
_ => 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn single_player(&self) -> u8 {
|
pub fn player_mode(&self) -> PlayerMode {
|
||||||
match self {
|
match self {
|
||||||
RoomMode::Single {..} => 1,
|
RoomMode::Single {..} => PlayerMode::Single,
|
||||||
_ => 0,
|
_ => PlayerMode::Multi,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,13 +302,12 @@ pub struct RoomState {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub password: [u16; 16],
|
pub password: [u16; 16],
|
||||||
pub maps: Maps,
|
pub maps: Maps,
|
||||||
pub drop_table: Box<DropTable<rand_chacha::ChaCha20Rng>>,
|
pub drop_table: Box<DropTable>,
|
||||||
pub section_id: SectionID,
|
pub section_id: SectionID,
|
||||||
pub random_seed: u32,
|
pub random_seed: u32,
|
||||||
pub bursting: bool,
|
pub bursting: bool,
|
||||||
pub monster_stats: Box<HashMap<MonsterType, MonsterStats>>,
|
pub monster_stats: Box<HashMap<MonsterType, MonsterStats>>,
|
||||||
pub map_areas: MapAreaLookup,
|
pub map_areas: MapAreaLookup,
|
||||||
pub rare_monster_table: Box<RareMonsterAppearTable>,
|
|
||||||
pub quest_group: QuestCategoryType,
|
pub quest_group: QuestCategoryType,
|
||||||
pub quests: Vec<quests::QuestList>,
|
pub quests: Vec<quests::QuestList>,
|
||||||
// items on ground
|
// items on ground
|
||||||
@ -343,7 +349,12 @@ impl RoomState {
|
|||||||
self.quest_group = QuestCategoryType::from(group);
|
self.quest_group = QuestCategoryType::from(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_create_room(create_room: &libpso::packet::ship::CreateRoom, section_id: SectionID, event: ShipEvent) -> Result<RoomState, RoomCreationError> {
|
pub fn from_create_room(create_room: &libpso::packet::ship::CreateRoom,
|
||||||
|
map_builder: Arc<Box<dyn Fn(RoomMode, ShipEvent) -> Maps + Send + Sync>>,
|
||||||
|
drop_table_builder: Arc<Box<dyn Fn(Episode, Difficulty, SectionID) -> DropTable + Send + Sync>>,
|
||||||
|
section_id: SectionID,
|
||||||
|
event: ShipEvent)
|
||||||
|
-> Result<RoomState, RoomCreationError> {
|
||||||
if [create_room.battle, create_room.challenge, create_room.single_player].iter().sum::<u8>() > 1 {
|
if [create_room.battle, create_room.challenge, create_room.single_player].iter().sum::<u8>() > 1 {
|
||||||
return Err(RoomCreationError::InvalidMode)
|
return Err(RoomCreationError::InvalidMode)
|
||||||
}
|
}
|
||||||
@ -372,7 +383,6 @@ impl RoomState {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode());
|
|
||||||
|
|
||||||
// push the usual set of quests for the selected mode
|
// push the usual set of quests for the selected mode
|
||||||
let mut qpath = PathBuf::from("data/quests/bb");
|
let mut qpath = PathBuf::from("data/quests/bb");
|
||||||
@ -401,18 +411,15 @@ impl RoomState {
|
|||||||
room_quests.push(quest_list);
|
room_quests.push(quest_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Ok(RoomState {
|
Ok(RoomState {
|
||||||
monster_stats: Box::new(load_monster_stats_table(&room_mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(room_mode))?),
|
monster_stats: Box::new(load_monster_stats_table(&room_mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(room_mode))?),
|
||||||
mode: room_mode,
|
mode: room_mode,
|
||||||
random_seed: rand::thread_rng().gen(),
|
random_seed: rand::thread_rng().gen(),
|
||||||
rare_monster_table: Box::new(rare_monster_table.clone()),
|
|
||||||
name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(),
|
name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(),
|
||||||
password: create_room.password,
|
password: create_room.password,
|
||||||
maps: Maps::new(room_mode, &rare_monster_table, event),
|
maps: map_builder(room_mode, event),
|
||||||
section_id,
|
section_id,
|
||||||
drop_table: Box::new(DropTable::new(room_mode.episode(), room_mode.difficulty(), section_id)),
|
drop_table: Box::new(drop_table_builder(room_mode.episode(), room_mode.difficulty(), section_id)),
|
||||||
bursting: false,
|
bursting: false,
|
||||||
map_areas: MapAreaLookup::new(&room_mode.episode()),
|
map_areas: MapAreaLookup::new(&room_mode.episode()),
|
||||||
quest_group: QuestCategoryType::Standard,
|
quest_group: QuestCategoryType::Standard,
|
||||||
|
@ -4,7 +4,6 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use async_std::channel;
|
use async_std::channel;
|
||||||
use async_std::sync::{Arc, Mutex, RwLock};
|
use async_std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@ -13,24 +12,19 @@ use libpso::packet::login::{RedirectClient, Login, LoginResponse, ShipList};
|
|||||||
use libpso::packet::messages::*;
|
use libpso::packet::messages::*;
|
||||||
use libpso::{PacketParseError, PSOPacket};
|
use libpso::{PacketParseError, PSOPacket};
|
||||||
use libpso::crypto::bb::PSOBBCipher;
|
use libpso::crypto::bb::PSOBBCipher;
|
||||||
|
|
||||||
use libpso::packet::ship::{BLOCK_MENU_ID, ROOM_MENU_ID};
|
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::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage};
|
use crate::common::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage};
|
||||||
|
|
||||||
use crate::login::character::SHIP_MENU_ID;
|
use crate::login::character::SHIP_MENU_ID;
|
||||||
|
|
||||||
use crate::entity::gateway::{EntityGateway, GatewayError};
|
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||||
use crate::entity::character::SectionID;
|
use crate::entity::character::SectionID;
|
||||||
|
|
||||||
use crate::ship::location::{ClientLocation, RoomLobby, ClientLocationError, RoomId};
|
use crate::ship::location::{ClientLocation, RoomLobby, ClientLocationError, RoomId};
|
||||||
|
use crate::ship::drops::DropTable;
|
||||||
use crate::ship::items;
|
use crate::ship::items;
|
||||||
use crate::ship::room;
|
use crate::ship::room;
|
||||||
use crate::ship::map::{MapsError, MapAreaError};
|
use crate::ship::map::{Maps, MapsError, MapAreaError, generate_free_roam_maps};
|
||||||
use crate::ship::packet::handler;
|
use crate::ship::packet::handler;
|
||||||
use crate::ship::shops::{WeaponShop, ToolShop, ArmorShop};
|
use crate::ship::shops::{WeaponShop, ToolShop, ArmorShop};
|
||||||
use crate::ship::trade::TradeState;
|
use crate::ship::trade::TradeState;
|
||||||
@ -162,11 +156,13 @@ pub enum ShipError {
|
|||||||
SendError(#[from] async_std::channel::SendError<ShipMessage>),
|
SendError(#[from] async_std::channel::SendError<ShipMessage>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
impl<I: Into<ClientLocationError>> From<I> for ShipError {
|
impl<I: Into<ClientLocationError>> From<I> for ShipError {
|
||||||
fn from(other: I) -> ShipError {
|
fn from(other: I) -> ShipError {
|
||||||
ShipError::ClientLocationError(other.into())
|
ShipError::ClientLocationError(other.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -375,6 +371,8 @@ pub struct ShipServerStateBuilder<EG: EntityGateway + Clone + 'static> {
|
|||||||
port: Option<u16>,
|
port: Option<u16>,
|
||||||
auth_token: Option<AuthToken>,
|
auth_token: Option<AuthToken>,
|
||||||
event: Option<ShipEvent>,
|
event: Option<ShipEvent>,
|
||||||
|
map_builder: Option<Box<dyn Fn(room::RoomMode, ShipEvent) -> Maps + Send + Sync>>,
|
||||||
|
drop_table_builder: Option<Box<dyn Fn(room::Episode, room::Difficulty, SectionID) -> DropTable + Send + Sync>>,
|
||||||
num_blocks: usize,
|
num_blocks: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,6 +385,8 @@ impl<EG: EntityGateway + Clone + 'static> Default for ShipServerStateBuilder<EG>
|
|||||||
port: None,
|
port: None,
|
||||||
auth_token: None,
|
auth_token: None,
|
||||||
event: None,
|
event: None,
|
||||||
|
map_builder: None,
|
||||||
|
drop_table_builder: None,
|
||||||
num_blocks: 2,
|
num_blocks: 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,6 +429,18 @@ impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn map_builder(mut self, map_builder: Box<dyn Fn(room::RoomMode, ShipEvent) -> Maps + Send + Sync>) -> ShipServerStateBuilder<EG> {
|
||||||
|
self.map_builder = Some(map_builder);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn drop_table_builder(mut self, drop_table_builder: Box<dyn Fn(room::Episode, room::Difficulty, SectionID) -> DropTable + Send + Sync>) -> ShipServerStateBuilder<EG> {
|
||||||
|
self.drop_table_builder = Some(drop_table_builder);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn blocks(mut self, num_blocks: usize) -> ShipServerStateBuilder<EG> {
|
pub fn blocks(mut self, num_blocks: usize) -> ShipServerStateBuilder<EG> {
|
||||||
self.num_blocks = num_blocks;
|
self.num_blocks = num_blocks;
|
||||||
@ -447,6 +459,8 @@ impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> {
|
|||||||
shops: ItemShops::default(),
|
shops: ItemShops::default(),
|
||||||
blocks: Blocks(blocks),
|
blocks: Blocks(blocks),
|
||||||
event: self.event.unwrap_or(ShipEvent::None),
|
event: self.event.unwrap_or(ShipEvent::None),
|
||||||
|
map_builder: Arc::new(self.map_builder.unwrap_or(Box::new(generate_free_roam_maps))),
|
||||||
|
drop_table_builder: Arc::new(self.drop_table_builder.unwrap_or(Box::new(DropTable::new))),
|
||||||
|
|
||||||
auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())),
|
auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())),
|
||||||
ship_list: Arc::new(RwLock::new(Vec::new())),
|
ship_list: Arc::new(RwLock::new(Vec::new())),
|
||||||
@ -467,13 +481,13 @@ pub struct Block {
|
|||||||
pub struct Blocks(pub Vec<Block>);
|
pub struct Blocks(pub Vec<Block>);
|
||||||
|
|
||||||
impl Blocks {
|
impl Blocks {
|
||||||
async fn get_from_client(&mut self, id: ClientId, clients: &Clients) -> Result<&mut Block, ShipError> {
|
async fn get_from_client(&mut self, id: ClientId, clients: &Clients) -> Result<&mut Block, anyhow::Error> {
|
||||||
let block = clients.with(id, |client| Box::pin(async move {
|
let block = clients.with(id, |client| Box::pin(async move {
|
||||||
client.block
|
client.block
|
||||||
})).await?;
|
})).await?;
|
||||||
self.0
|
self.0
|
||||||
.get_mut(block)
|
.get_mut(block)
|
||||||
.ok_or_else(|| ShipError::InvalidBlock(block))
|
.ok_or_else(|| ShipError::InvalidBlock(block).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -495,6 +509,8 @@ pub struct ShipServerState<EG: EntityGateway + Clone + 'static> {
|
|||||||
ship_list: Arc<RwLock<Vec<Ship>>>,
|
ship_list: Arc<RwLock<Vec<Ship>>>,
|
||||||
shipgate_sender: Option<channel::Sender<ShipMessage>>,
|
shipgate_sender: Option<channel::Sender<ShipMessage>>,
|
||||||
trades: TradeState,
|
trades: TradeState,
|
||||||
|
map_builder: Arc<Box<dyn Fn(room::RoomMode, ShipEvent) -> Maps + Send + Sync>>,
|
||||||
|
drop_table_builder: Arc<Box<dyn Fn(room::Episode, room::Difficulty, SectionID) -> DropTable + Send + Sync>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<EG: EntityGateway + Clone + 'static> ShipServerState<EG> {
|
impl<EG: EntityGateway + Clone + 'static> ShipServerState<EG> {
|
||||||
@ -665,12 +681,12 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
|||||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||||
match menuselect.menu {
|
match menuselect.menu {
|
||||||
SHIP_MENU_ID => {
|
SHIP_MENU_ID => {
|
||||||
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).await.into_iter().into_iter().flatten();
|
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).await.into_iter().flatten();
|
||||||
let select_ship = handler::ship::selected_ship(id, menuselect, &self.ship_list).await?;
|
let select_ship = handler::ship::selected_ship(id, menuselect, &self.ship_list).await?;
|
||||||
leave_lobby.chain(select_ship).collect()
|
leave_lobby.chain(select_ship).collect()
|
||||||
}
|
}
|
||||||
BLOCK_MENU_ID => {
|
BLOCK_MENU_ID => {
|
||||||
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).await.into_iter().into_iter().flatten();
|
let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).await.into_iter().flatten();
|
||||||
let select_block = handler::lobby::block_selected(id, menuselect, &self.clients, &self.item_state).await?.into_iter();
|
let select_block = handler::lobby::block_selected(id, menuselect, &self.clients, &self.item_state).await?.into_iter();
|
||||||
leave_lobby.chain(select_block).collect()
|
leave_lobby.chain(select_block).collect()
|
||||||
}
|
}
|
||||||
@ -721,7 +737,7 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
|||||||
},
|
},
|
||||||
RecvShipPacket::CreateRoom(create_room) => {
|
RecvShipPacket::CreateRoom(create_room) => {
|
||||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
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, self.event).await?
|
handler::room::create_room(id, create_room, &mut block.client_location, &self.clients, &mut self.item_state, &block.rooms, self.map_builder.clone(), self.drop_table_builder.clone(), self.event).await?
|
||||||
},
|
},
|
||||||
RecvShipPacket::RoomNameRequest(_req) => {
|
RecvShipPacket::RoomNameRequest(_req) => {
|
||||||
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
let block = self.blocks.get_from_client(id, &self.clients).await?;
|
||||||
@ -834,6 +850,8 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
|
|||||||
self.item_state.remove_character_from_room(&client.character).await
|
self.item_state.remove_character_from_room(&client.character).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block.client_location.remove_client_from_area(id).await?;
|
||||||
|
|
||||||
Ok(neighbors.into_iter().map(|n| {
|
Ok(neighbors.into_iter().map(|n| {
|
||||||
(n.client, pkt.clone())
|
(n.client, pkt.clone())
|
||||||
}).collect())
|
}).collect())
|
||||||
|
@ -13,6 +13,7 @@ use libpso::packet::login::{Login, Session};
|
|||||||
use libpso::{utf8_to_array, utf8_to_utf16_array};
|
use libpso::{utf8_to_array, utf8_to_utf16_array};
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: remove kb_conf_preset
|
||||||
pub async fn new_user_character<EG: EntityGateway + Clone>(entity_gateway: &mut EG, username: &str, password: &str, kb_conf_preset: usize) -> (UserAccountEntity, CharacterEntity) {
|
pub async fn new_user_character<EG: EntityGateway + Clone>(entity_gateway: &mut EG, username: &str, password: &str, kb_conf_preset: usize) -> (UserAccountEntity, CharacterEntity) {
|
||||||
let new_user = NewUserAccountEntity {
|
let new_user = NewUserAccountEntity {
|
||||||
email: format!("{}@pso.com", username),
|
email: format!("{}@pso.com", username),
|
||||||
@ -26,7 +27,7 @@ pub async fn new_user_character<EG: EntityGateway + Clone>(entity_gateway: &mut
|
|||||||
let user = entity_gateway.create_user(new_user).await.unwrap();
|
let user = entity_gateway.create_user(new_user).await.unwrap();
|
||||||
let new_settings = NewUserSettingsEntity::new(user.id);
|
let new_settings = NewUserSettingsEntity::new(user.id);
|
||||||
let _settings = entity_gateway.create_user_settings(new_settings).await.unwrap();
|
let _settings = entity_gateway.create_user_settings(new_settings).await.unwrap();
|
||||||
let new_character = NewCharacterEntity::new(user.id, kb_conf_preset);
|
let new_character = NewCharacterEntity::new(user.id);
|
||||||
let character = entity_gateway.create_character(new_character).await.unwrap();
|
let character = entity_gateway.create_character(new_character).await.unwrap();
|
||||||
entity_gateway.set_character_meseta(&character.id, Meseta(0)).await.unwrap();
|
entity_gateway.set_character_meseta(&character.id, Meseta(0)).await.unwrap();
|
||||||
entity_gateway.set_bank_meseta(&character.id, &BankName("".into()), Meseta(0)).await.unwrap();
|
entity_gateway.set_bank_meseta(&character.id, &BankName("".into()), Meseta(0)).await.unwrap();
|
||||||
|
@ -31,26 +31,6 @@ async fn test_save_options() {
|
|||||||
assert!(char.option_flags == 12345);
|
assert!(char.option_flags == 12345);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn test_default3_keyboard_mappings() {
|
|
||||||
/*
|
|
||||||
check if keyboard is set to default3 when specified. this will only occur for things like creating characters from the web page.
|
|
||||||
normal client behaviour will simply use default1 when creating a character.
|
|
||||||
gamepad only has 1 default config
|
|
||||||
*/
|
|
||||||
let mut entity_gateway = InMemoryGateway::default();
|
|
||||||
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 3).await;
|
|
||||||
assert!(char1.keyboard_config.as_bytes() == DEFAULT_KEYBOARD_CONFIG3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn test_invalid_keyboard_preset_value() {
|
|
||||||
// check if keyboard_config self-corrects to DEFAULT1 if an invalid value (>4) is given
|
|
||||||
let mut entity_gateway = InMemoryGateway::default();
|
|
||||||
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 10).await;
|
|
||||||
assert!(char1.keyboard_config.as_bytes() == DEFAULT_KEYBOARD_CONFIG1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_change_keyboard_mappings() {
|
async fn test_change_keyboard_mappings() {
|
||||||
let mut entity_gateway = InMemoryGateway::default();
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
@ -63,7 +43,8 @@ async fn test_change_keyboard_mappings() {
|
|||||||
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;
|
||||||
|
|
||||||
assert!(char1.keyboard_config.as_bytes() == DEFAULT_KEYBOARD_CONFIG2);
|
let settings = entity_gateway.get_user_settings_by_user(&user1).await.unwrap();
|
||||||
|
assert!(settings.settings.keyboard_config == DEFAULT_KEYBOARD_CONFIG1);
|
||||||
|
|
||||||
// update from default2 to default4
|
// update from default2 to default4
|
||||||
// the client simply sends the full 364 bytes...
|
// the client simply sends the full 364 bytes...
|
||||||
@ -95,8 +76,6 @@ async fn test_change_keyboard_mappings() {
|
|||||||
],
|
],
|
||||||
})).await.unwrap();
|
})).await.unwrap();
|
||||||
|
|
||||||
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
let settings = entity_gateway.get_user_settings_by_user(&user1).await.unwrap();
|
||||||
let char = characters[0].as_ref().unwrap();
|
assert!(settings.settings.keyboard_config == DEFAULT_KEYBOARD_CONFIG4);
|
||||||
|
|
||||||
assert!(char.keyboard_config.as_bytes() == DEFAULT_KEYBOARD_CONFIG4);
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,10 @@ use elseware::common::leveltable::CharacterLevelTable;
|
|||||||
use elseware::ship::ship::{ShipServerState, SendShipPacket, RecvShipPacket};
|
use elseware::ship::ship::{ShipServerState, SendShipPacket, RecvShipPacket};
|
||||||
use elseware::ship::monster::MonsterType;
|
use elseware::ship::monster::MonsterType;
|
||||||
use elseware::ship::location::RoomId;
|
use elseware::ship::location::RoomId;
|
||||||
|
use elseware::ship::map::variant::{MapVariant, MapVariantMode};
|
||||||
|
use elseware::ship::map::maps::Maps;
|
||||||
|
use elseware::ship::map::area::MapArea;
|
||||||
|
use elseware::ship::map::enemy::MapEnemy;
|
||||||
|
|
||||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||||
use libpso::packet::messages::*;
|
use libpso::packet::messages::*;
|
||||||
@ -19,36 +23,30 @@ async fn test_character_gains_exp() {
|
|||||||
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
.build());
|
.map_builder(Box::new(|_room_mode, _event| {
|
||||||
|
Maps::new(
|
||||||
|
vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)],
|
||||||
|
vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))],
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.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;
|
||||||
|
|
||||||
let (enemy_id, 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,
|
client: 0,
|
||||||
target: 16,
|
target: 16,
|
||||||
enemy_id: enemy_id as u16,
|
enemy_id: 0,
|
||||||
client_id: 0,
|
client_id: 0,
|
||||||
unused: 0,
|
unused: 0,
|
||||||
last_hitter: 1,
|
last_hitter: 1,
|
||||||
})))).await.unwrap();
|
})))).await.unwrap();
|
||||||
|
|
||||||
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
||||||
assert!(exp == client.character.exp);
|
assert!(13 == client.character.exp);
|
||||||
})).await.unwrap();
|
})).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,30 +59,29 @@ async fn test_character_levels_up() {
|
|||||||
entity_gateway.save_character(&char1).await.unwrap();
|
entity_gateway.save_character(&char1).await.unwrap();
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
.build());
|
.map_builder(Box::new(|_room_mode, _event| {
|
||||||
|
Maps::new(
|
||||||
|
vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)],
|
||||||
|
vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))],
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.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;
|
||||||
|
|
||||||
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,
|
client: 0 as u8,
|
||||||
target: 16,
|
target: 16,
|
||||||
enemy_id: enemy_id as u16,
|
enemy_id: 0 as u16,
|
||||||
client_id: 0,
|
client_id: 0,
|
||||||
unused: 0,
|
unused: 0,
|
||||||
last_hitter: 1,
|
last_hitter: 1,
|
||||||
})))).await.unwrap();
|
})))).await.unwrap();
|
||||||
|
|
||||||
assert!(matches!(levelup_pkt[1].1, SendShipPacket::Message(Message {msg: GameMessage::PlayerLevelUp(PlayerLevelUp {lvl: 2, ..})})));
|
assert!(matches!(levelup_pkt[1].1, SendShipPacket::Message(Message {msg: GameMessage::PlayerLevelUp(PlayerLevelUp {lvl: 1, ..})})));
|
||||||
|
|
||||||
let leveltable = CharacterLevelTable::default();
|
let leveltable = CharacterLevelTable::default();
|
||||||
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
||||||
@ -99,42 +96,32 @@ async fn test_character_levels_up_multiple_times() {
|
|||||||
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
.build());
|
.map_builder(Box::new(|_room_mode, _event| {
|
||||||
|
Maps::new(
|
||||||
|
vec![MapVariant::new(MapArea::DarkFalz, MapVariantMode::Online)],
|
||||||
|
vec![Some(MapEnemy::new(MonsterType::DarkFalz2, MapArea::DarkFalz))],
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.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;
|
||||||
|
|
||||||
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 {
|
|
||||||
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,
|
client: 0 as u8,
|
||||||
target: 16,
|
target: 16,
|
||||||
enemy_id: enemy_id as u16,
|
enemy_id: 0 as u16,
|
||||||
client_id: 0,
|
client_id: 0,
|
||||||
unused: 0,
|
unused: 0,
|
||||||
last_hitter: 1,
|
last_hitter: 1,
|
||||||
})))).await.unwrap();
|
})))).await.unwrap();
|
||||||
|
|
||||||
assert!(matches!(levelup_pkt[1].1, SendShipPacket::Message(Message {msg: GameMessage::PlayerLevelUp(PlayerLevelUp {lvl: 8, ..})})));
|
assert!(matches!(levelup_pkt[1].1, SendShipPacket::Message(Message {msg: GameMessage::PlayerLevelUp(PlayerLevelUp {lvl: 7, ..})})));
|
||||||
|
|
||||||
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
||||||
assert!(exp == client.character.exp);
|
assert!(3000 == client.character.exp);
|
||||||
})).await.unwrap();
|
})).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,8 +133,15 @@ async fn test_one_character_gets_full_exp_and_other_attacker_gets_partial() {
|
|||||||
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;
|
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;
|
||||||
|
|
||||||
let mut ship = Box::new(ShipServerState::builder()
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
.gateway(entity_gateway.clone())
|
.gateway(entity_gateway.clone())
|
||||||
.build());
|
.map_builder(Box::new(|_room_mode, _event| {
|
||||||
|
Maps::new(
|
||||||
|
vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)],
|
||||||
|
vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))],
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.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;
|
||||||
|
|
||||||
@ -157,43 +151,28 @@ async fn test_one_character_gets_full_exp_and_other_attacker_gets_partial() {
|
|||||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||||
join_room(&mut ship, ClientId(2), 0).await;
|
join_room(&mut ship, ClientId(2), 0).await;
|
||||||
|
|
||||||
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 {
|
|
||||||
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();
|
|
||||||
|
|
||||||
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,
|
client: 0,
|
||||||
target: 16,
|
target: 16,
|
||||||
enemy_id: enemy_id as u16,
|
enemy_id: 0,
|
||||||
client_id: 0,
|
client_id: 0,
|
||||||
unused: 0,
|
unused: 0,
|
||||||
last_hitter: 1,
|
last_hitter: 1,
|
||||||
})))).await.unwrap();
|
})))).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,
|
client: 0,
|
||||||
target: 16,
|
target: 16,
|
||||||
enemy_id: enemy_id as u16,
|
enemy_id: 0,
|
||||||
client_id: 0,
|
client_id: 0,
|
||||||
unused: 0,
|
unused: 0,
|
||||||
last_hitter: 0,
|
last_hitter: 0,
|
||||||
})))).await.unwrap();
|
})))).await.unwrap();
|
||||||
|
|
||||||
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
ship.clients.with(ClientId(1), |client| Box::pin(async move {
|
||||||
assert!(client.character.exp == exp);
|
assert_eq!(client.character.exp, 13);
|
||||||
})).await.unwrap();
|
})).await.unwrap();
|
||||||
ship.clients.with(ClientId(2), |client| Box::pin(async move {
|
ship.clients.with(ClientId(2), |client| Box::pin(async move {
|
||||||
assert!(client.character.exp == (exp as f32 * 0.8) as u32);
|
assert_eq!(client.character.exp, 10);
|
||||||
})).await.unwrap();
|
})).await.unwrap();
|
||||||
}
|
}
|
||||||
|
210
tests/test_item_drop.rs
Normal file
210
tests/test_item_drop.rs
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
use elseware::common::serverstate::{ClientId, ServerState};
|
||||||
|
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
||||||
|
use elseware::entity::character::SectionID;
|
||||||
|
use elseware::common::leveltable::CharacterLevelTable;
|
||||||
|
use elseware::ship::ship::{ShipServerState, SendShipPacket, RecvShipPacket};
|
||||||
|
use elseware::ship::room::{Episode, Difficulty};
|
||||||
|
use elseware::ship::monster::MonsterType;
|
||||||
|
use elseware::ship::location::RoomId;
|
||||||
|
use elseware::ship::drops::{DropTable, MonsterDropStats, MonsterDropType};
|
||||||
|
use elseware::ship::drops::rare_drop_table::{RareDropTable, RareDropRate, RareDropItem};
|
||||||
|
use elseware::ship::map::{Maps, MapVariant, MapArea, MapVariantMode, MapEnemy};
|
||||||
|
use elseware::entity::item::weapon::WeaponType;
|
||||||
|
|
||||||
|
use libpso::packet::ship::*;
|
||||||
|
use libpso::packet::messages::*;
|
||||||
|
|
||||||
|
#[path = "common.rs"]
|
||||||
|
mod common;
|
||||||
|
use common::*;
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_enemy_drops_item() {
|
||||||
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
|
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
|
|
||||||
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
|
.gateway(entity_gateway.clone())
|
||||||
|
.map_builder(Box::new(|_room_mode, _event| {
|
||||||
|
Maps::new(
|
||||||
|
vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)],
|
||||||
|
vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))],
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.drop_table_builder(Box::new(|episode, difficulty, section_id| {
|
||||||
|
DropTable::builder()
|
||||||
|
.monster_stat(MonsterType::Hildebear, MonsterDropStats {
|
||||||
|
dar: 100,
|
||||||
|
drop_type: MonsterDropType::Weapon,
|
||||||
|
min_meseta: 0,
|
||||||
|
max_meseta: 0,
|
||||||
|
})
|
||||||
|
.rare_table(RareDropTable::builder()
|
||||||
|
.rate(MonsterType::Hildebear, RareDropRate {
|
||||||
|
rate: 1.0,
|
||||||
|
item: RareDropItem::Weapon(WeaponType::DarkFlow)
|
||||||
|
})
|
||||||
|
.build(episode, difficulty, section_id))
|
||||||
|
.build(episode, difficulty, section_id)
|
||||||
|
}))
|
||||||
|
.build());
|
||||||
|
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||||
|
join_lobby(&mut ship, ClientId(1)).await;
|
||||||
|
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||||
|
|
||||||
|
let pkt = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::RequestItem(RequestItem {
|
||||||
|
client: 0,
|
||||||
|
target: 0,
|
||||||
|
map_area: 2,
|
||||||
|
pt_index: 0,
|
||||||
|
enemy_id: 0,
|
||||||
|
x: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
})))).await.unwrap();
|
||||||
|
|
||||||
|
match &pkt[0].1 {
|
||||||
|
SendShipPacket::Message(Message{msg: GameMessage::ItemDrop(item_drop)}) => {
|
||||||
|
assert_eq!(item_drop.item_id, 0x810001);
|
||||||
|
assert_eq!(item_drop.item_bytes[1], 0x9D);
|
||||||
|
},
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_enemy_drops_item_for_two_players() {
|
||||||
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
|
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
|
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;
|
||||||
|
|
||||||
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
|
.gateway(entity_gateway.clone())
|
||||||
|
.map_builder(Box::new(|_room_mode, _event| {
|
||||||
|
Maps::new(
|
||||||
|
vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)],
|
||||||
|
vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))],
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.drop_table_builder(Box::new(|episode, difficulty, section_id| {
|
||||||
|
DropTable::builder()
|
||||||
|
.monster_stat(MonsterType::Hildebear, MonsterDropStats {
|
||||||
|
dar: 100,
|
||||||
|
drop_type: MonsterDropType::Weapon,
|
||||||
|
min_meseta: 0,
|
||||||
|
max_meseta: 0,
|
||||||
|
})
|
||||||
|
.rare_table(RareDropTable::builder()
|
||||||
|
.rate(MonsterType::Hildebear, RareDropRate {
|
||||||
|
rate: 1.0,
|
||||||
|
item: RareDropItem::Weapon(WeaponType::DarkFlow)
|
||||||
|
})
|
||||||
|
.build(episode, difficulty, section_id))
|
||||||
|
.build(episode, difficulty, section_id)
|
||||||
|
}))
|
||||||
|
.build());
|
||||||
|
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||||
|
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
|
||||||
|
join_lobby(&mut ship, ClientId(1)).await;
|
||||||
|
join_lobby(&mut ship, ClientId(2)).await;
|
||||||
|
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||||
|
join_room(&mut ship, ClientId(2), 0).await;
|
||||||
|
|
||||||
|
let pkt = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::RequestItem(RequestItem {
|
||||||
|
client: 0,
|
||||||
|
target: 0,
|
||||||
|
map_area: 2,
|
||||||
|
pt_index: 0,
|
||||||
|
enemy_id: 0,
|
||||||
|
x: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
})))).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(pkt[0].0, ClientId(1));
|
||||||
|
match &pkt[0].1 {
|
||||||
|
SendShipPacket::Message(Message{msg: GameMessage::ItemDrop(item_drop)}) => {
|
||||||
|
assert_eq!(item_drop.item_id, 0x810001);
|
||||||
|
assert_eq!(item_drop.item_bytes[1], 0x9D);
|
||||||
|
},
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
assert_eq!(pkt[1].0, ClientId(2));
|
||||||
|
match &pkt[1].1 {
|
||||||
|
SendShipPacket::Message(Message{msg: GameMessage::ItemDrop(item_drop)}) => {
|
||||||
|
assert_eq!(item_drop.item_id, 0x810002);
|
||||||
|
assert_eq!(item_drop.item_bytes[1], 0x9D);
|
||||||
|
},
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_enemy_drops_item_for_two_players_and_pick_up() {
|
||||||
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
|
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
|
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;
|
||||||
|
|
||||||
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
|
.gateway(entity_gateway.clone())
|
||||||
|
.map_builder(Box::new(|_room_mode, _event| {
|
||||||
|
Maps::new(
|
||||||
|
vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)],
|
||||||
|
vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))],
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.drop_table_builder(Box::new(|episode, difficulty, section_id| {
|
||||||
|
DropTable::builder()
|
||||||
|
.monster_stat(MonsterType::Hildebear, MonsterDropStats {
|
||||||
|
dar: 100,
|
||||||
|
drop_type: MonsterDropType::Weapon,
|
||||||
|
min_meseta: 0,
|
||||||
|
max_meseta: 0,
|
||||||
|
})
|
||||||
|
.rare_table(RareDropTable::builder()
|
||||||
|
.rate(MonsterType::Hildebear, RareDropRate {
|
||||||
|
rate: 1.0,
|
||||||
|
item: RareDropItem::Weapon(WeaponType::DarkFlow)
|
||||||
|
})
|
||||||
|
.build(episode, difficulty, section_id))
|
||||||
|
.build(episode, difficulty, section_id)
|
||||||
|
}))
|
||||||
|
.build());
|
||||||
|
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||||
|
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
|
||||||
|
join_lobby(&mut ship, ClientId(1)).await;
|
||||||
|
join_lobby(&mut ship, ClientId(2)).await;
|
||||||
|
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||||
|
join_room(&mut ship, ClientId(2), 0).await;
|
||||||
|
|
||||||
|
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::RequestItem(RequestItem {
|
||||||
|
client: 0,
|
||||||
|
target: 0,
|
||||||
|
map_area: 2,
|
||||||
|
pt_index: 0,
|
||||||
|
enemy_id: 0,
|
||||||
|
x: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
})))).await.unwrap();
|
||||||
|
|
||||||
|
ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||||
|
client: 0,
|
||||||
|
target: 0,
|
||||||
|
item_id: 0x810002,
|
||||||
|
map_area: 0,
|
||||||
|
unknown: [0; 3]
|
||||||
|
})))).await.unwrap();
|
||||||
|
|
||||||
|
ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||||
|
client: 0,
|
||||||
|
target: 0,
|
||||||
|
item_id: 0x810001,
|
||||||
|
map_area: 0,
|
||||||
|
unknown: [0; 3]
|
||||||
|
})))).await.unwrap();
|
||||||
|
|
||||||
|
}
|
@ -2,6 +2,7 @@ use elseware::common::serverstate::{ClientId, ServerState};
|
|||||||
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
||||||
use elseware::entity::item;
|
use elseware::entity::item;
|
||||||
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
|
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
|
||||||
|
use elseware::entity::character::TechLevel;
|
||||||
//use elseware::ship::items::{ClientItemId, ActiveItemEntityId, HeldItemType, FloorItemType};
|
//use elseware::ship::items::{ClientItemId, ActiveItemEntityId, HeldItemType, FloorItemType};
|
||||||
|
|
||||||
use libpso::packet::ship::*;
|
use libpso::packet::ship::*;
|
||||||
@ -304,6 +305,92 @@ async fn test_jackolantern() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_use_barta_1() {
|
||||||
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
|
|
||||||
|
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
|
||||||
|
|
||||||
|
/*
|
||||||
|
let mut p1_inv = Vec::new();
|
||||||
|
for tool in vec![item::tool::ToolType::PowerMaterial, item::tool::ToolType::].into_iter() {
|
||||||
|
let mut item = Vec::new();
|
||||||
|
for _ in 0..5usize {
|
||||||
|
item.push(entity_gateway.create_item(
|
||||||
|
item::NewItemEntity {
|
||||||
|
item: item::ItemDetail::Tool(
|
||||||
|
item::tool::Tool {
|
||||||
|
tool: tool
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}).await.unwrap());
|
||||||
|
}
|
||||||
|
p1_inv.push(item::InventoryItemEntity::Stacked(item));
|
||||||
|
}*/
|
||||||
|
let inv = vec![
|
||||||
|
entity_gateway.create_item(
|
||||||
|
item::NewItemEntity {
|
||||||
|
item: item::ItemDetail::TechniqueDisk(
|
||||||
|
item::tech::TechniqueDisk {
|
||||||
|
tech: item::tech::Technique::Foie,
|
||||||
|
level: 3,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
).await.unwrap(),
|
||||||
|
entity_gateway.create_item(
|
||||||
|
item::NewItemEntity {
|
||||||
|
item: item::ItemDetail::TechniqueDisk(
|
||||||
|
item::tech::TechniqueDisk {
|
||||||
|
tech: item::tech::Technique::Barta,
|
||||||
|
level: 4,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
).await.unwrap(),
|
||||||
|
entity_gateway.create_item(
|
||||||
|
item::NewItemEntity {
|
||||||
|
item: item::ItemDetail::TechniqueDisk(
|
||||||
|
item::tech::TechniqueDisk {
|
||||||
|
tech: item::tech::Technique::Zonde,
|
||||||
|
level: 5,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
).await.unwrap()
|
||||||
|
];
|
||||||
|
|
||||||
|
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inv)).await.unwrap();
|
||||||
|
|
||||||
|
let mut ship = Box::new(ShipServerState::builder()
|
||||||
|
.gateway(entity_gateway.clone())
|
||||||
|
.build());
|
||||||
|
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||||
|
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 {
|
||||||
|
client: 0,
|
||||||
|
target: 0,
|
||||||
|
item_id: 0x10000,
|
||||||
|
})))).await.unwrap();
|
||||||
|
|
||||||
|
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
|
||||||
|
client: 0,
|
||||||
|
target: 0,
|
||||||
|
item_id: 0x10002,
|
||||||
|
})))).await.unwrap();
|
||||||
|
|
||||||
|
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||||
|
let char = characters[0].as_ref().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(char.techs.techs.len(), 2);
|
||||||
|
assert_eq!(char.techs.techs.get(&item::tech::Technique::Foie).unwrap(), &TechLevel(3));
|
||||||
|
assert_eq!(char.techs.techs.get(&item::tech::Technique::Zonde).unwrap(), &TechLevel(5));
|
||||||
|
assert!(char.techs.techs.get(&item::tech::Technique::Barta).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: tests for ALL ITEMS WOW
|
// TODO: tests for ALL ITEMS WOW
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -97,6 +97,7 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_load_rare_monster_default_appear_rates() {
|
async fn test_load_rare_monster_default_appear_rates() {
|
||||||
let mut entity_gateway = InMemoryGateway::default();
|
let mut entity_gateway = InMemoryGateway::default();
|
||||||
@ -116,6 +117,7 @@ async fn test_load_rare_monster_default_appear_rates() {
|
|||||||
}
|
}
|
||||||
})).await.unwrap();
|
})).await.unwrap();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_set_valid_quest_group() {
|
async fn test_set_valid_quest_group() {
|
||||||
|
@ -1143,7 +1143,8 @@ async fn test_player_cant_sell_if_meseta_would_go_over_max() {
|
|||||||
amount: 1,
|
amount: 1,
|
||||||
})))).await.err().unwrap();
|
})))).await.err().unwrap();
|
||||||
//assert_eq!(ack, ShipError::ItemStateError(ItemStateError::FullOfMeseta));
|
//assert_eq!(ack, ShipError::ItemStateError(ItemStateError::FullOfMeseta));
|
||||||
assert!(matches!(ack.downcast::<ShipError>().unwrap(), ShipError::ItemStateError(ItemStateError::FullOfMeseta)));
|
assert!(matches!(ack.downcast::<ItemStateError>().unwrap(), ItemStateError::FullOfMeseta));
|
||||||
|
//assert!(matches!(ack.downcast::<ShipError>().unwrap(), ShipError::ItemStateError(ItemStateError::FullOfMeseta)));
|
||||||
|
|
||||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||||
assert_eq!(c1_meseta.0, 999995);
|
assert_eq!(c1_meseta.0, 999995);
|
||||||
|
@ -3155,7 +3155,7 @@ async fn test_client_tries_to_start_two_trades() {
|
|||||||
trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0)
|
trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0)
|
||||||
})))).await.err().unwrap();
|
})))).await.err().unwrap();
|
||||||
|
|
||||||
assert!(matches!(ack.downcast::<ShipError>().unwrap(), ShipError::TradeError(TradeError::ClientAlreadyInTrade)));
|
assert!(matches!(ack.downcast::<TradeError>().unwrap(), TradeError::ClientAlreadyInTrade));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
@ -3187,14 +3187,14 @@ async fn test_client_tries_trading_with_client_already_trading() {
|
|||||||
target: 0,
|
target: 0,
|
||||||
trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0)
|
trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0)
|
||||||
})))).await.err().unwrap();
|
})))).await.err().unwrap();
|
||||||
assert!(matches!(ack.downcast::<ShipError>().unwrap(), ShipError::TradeError(TradeError::OtherAlreadyInTrade)));
|
assert!(matches!(ack.downcast::<TradeError>().unwrap(), TradeError::OtherAlreadyInTrade));
|
||||||
|
|
||||||
let ack = ship.handle(ClientId(3), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
|
let ack = ship.handle(ClientId(3), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
|
||||||
client: 2,
|
client: 2,
|
||||||
target: 0,
|
target: 0,
|
||||||
trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 1)
|
trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 1)
|
||||||
})))).await.err().unwrap();
|
})))).await.err().unwrap();
|
||||||
assert!(matches!(ack.downcast::<ShipError>().unwrap(), ShipError::TradeError(TradeError::OtherAlreadyInTrade)));
|
assert!(matches!(ack.downcast::<TradeError>().unwrap(), TradeError::OtherAlreadyInTrade));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user