Merge pull request 'TRADING YEAH LETS GO' (#80) from trading 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: #80
This commit is contained in:
commit
26a2ef6492
@ -24,12 +24,12 @@ enum-utils = "0.1.2"
|
||||
derive_more = { version = "0.99.3", features = ["display"]}
|
||||
thiserror = "1.0.15"
|
||||
ages-prs = "0.1"
|
||||
async-trait = "0.1.41"
|
||||
async-trait = "0.1.51"
|
||||
lazy_static = "1.4.0"
|
||||
barrel = { version = "0.6.5", features = ["pg"] }
|
||||
refinery = { version = "0.5.0", features = ["postgres"] }
|
||||
sqlx = { version = "0.4.0", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] }
|
||||
strum = "0.19.5"
|
||||
strum_macros = "0.19"
|
||||
anyhow = "1.0.33"
|
||||
anyhow = { version = "1.0.47", features = ["backtrace"] }
|
||||
|
||||
|
@ -9,7 +9,7 @@ use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
|
||||
#[allow(unused_imports)]
|
||||
use elseware::entity::gateway::{EntityGateway, InMemoryGateway, PostgresGateway};
|
||||
use elseware::entity::character::NewCharacterEntity;
|
||||
use elseware::entity::item::{NewItemEntity, ItemDetail, ItemLocation};
|
||||
use elseware::entity::item::{NewItemEntity, ItemDetail, InventoryItemEntity};
|
||||
use elseware::common::interserver::AuthToken;
|
||||
|
||||
use elseware::entity::item;
|
||||
@ -67,13 +67,16 @@ fn main() {
|
||||
entity_gateway.create_user_settings(NewUserSettingsEntity::new(fake_user.id)).await.unwrap();
|
||||
let mut character = NewCharacterEntity::new(fake_user.id);
|
||||
character.name = format!("Test Char {}", i*2);
|
||||
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_bank_meseta(&character.id, item::BankName("".into()), item::Meseta(999999)).await.unwrap();
|
||||
let mut character = NewCharacterEntity::new(fake_user.id);
|
||||
character.slot = 2;
|
||||
character.name = "ItemRefactor".into();
|
||||
character.exp = 80000000;
|
||||
character.meseta = 999999;
|
||||
let character = entity_gateway.create_character(character).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();
|
||||
|
||||
for _ in 0..3 {
|
||||
entity_gateway.create_item(
|
||||
@ -87,10 +90,6 @@ fn main() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: character.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap();
|
||||
}
|
||||
|
||||
@ -102,10 +101,6 @@ fn main() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: character.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap();
|
||||
}
|
||||
|
||||
@ -122,9 +117,6 @@ fn main() {
|
||||
tekked: false,
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
let item1 = entity_gateway.create_item(
|
||||
NewItemEntity {
|
||||
@ -139,9 +131,6 @@ fn main() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
let item2_w = entity_gateway.create_item(
|
||||
NewItemEntity {
|
||||
@ -156,9 +145,6 @@ fn main() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
let item3 = entity_gateway.create_item(
|
||||
NewItemEntity {
|
||||
@ -173,34 +159,25 @@ fn main() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
let item4 = entity_gateway.create_item(
|
||||
NewItemEntity {
|
||||
item: ItemDetail::Weapon(
|
||||
item::weapon::Weapon {
|
||||
weapon: item::weapon::WeaponType::DarkFlow,
|
||||
grind: 5,
|
||||
special: Some(item::weapon::WeaponSpecial::Charge),
|
||||
grind: 0,
|
||||
special: None,
|
||||
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}),
|
||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
|
||||
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
let item5_m = entity_gateway.create_item(
|
||||
item::NewItemEntity {
|
||||
item: item::ItemDetail::Mag(item::mag::Mag::baby_mag(0)),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
for _ in 0..10usize {
|
||||
@ -211,9 +188,6 @@ fn main() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::FedToMag {
|
||||
mag: item5_m.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
entity_gateway.feed_mag(&item5_m.id, &fed_tool.id).await.unwrap();
|
||||
}
|
||||
@ -226,9 +200,6 @@ fn main() {
|
||||
tool: item::tool::ToolType::CellOfMag502,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
let cell = entity_gateway.create_item(
|
||||
item::NewItemEntity {
|
||||
@ -237,7 +208,6 @@ fn main() {
|
||||
tool: item::tool::ToolType::CellOfMag502,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Consumed,
|
||||
}).await.unwrap();
|
||||
entity_gateway.use_mag_cell(&item5_m.id, &cell.id).await.unwrap();
|
||||
|
||||
@ -254,10 +224,6 @@ fn main() {
|
||||
tekked: false,
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Bank {
|
||||
character_id: character.id,
|
||||
name: item::BankName("".to_string()),
|
||||
}
|
||||
}).await.unwrap();
|
||||
let item7_a = entity_gateway.create_item(
|
||||
NewItemEntity {
|
||||
@ -269,9 +235,6 @@ fn main() {
|
||||
slots: 4,
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}
|
||||
).await.unwrap();
|
||||
let item8_s = entity_gateway.create_item(
|
||||
@ -283,9 +246,6 @@ fn main() {
|
||||
evp: 0,
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}
|
||||
).await.unwrap();
|
||||
let item9_u0 = entity_gateway.create_item(
|
||||
@ -296,9 +256,6 @@ fn main() {
|
||||
modifier: Some(item::unit::UnitModifier::Minus),
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}
|
||||
).await.unwrap();
|
||||
let item10_u1 = entity_gateway.create_item(
|
||||
@ -309,9 +266,6 @@ fn main() {
|
||||
modifier: Some(item::unit::UnitModifier::Minus),
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}
|
||||
).await.unwrap();
|
||||
let item11_u2 = entity_gateway.create_item(
|
||||
@ -322,9 +276,6 @@ fn main() {
|
||||
modifier: Some(item::unit::UnitModifier::Minus),
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}
|
||||
).await.unwrap();
|
||||
let item12_u3 = entity_gateway.create_item(
|
||||
@ -335,9 +286,6 @@ fn main() {
|
||||
modifier: Some(item::unit::UnitModifier::Minus),
|
||||
}
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}
|
||||
).await.unwrap();
|
||||
let item13 = entity_gateway.create_item(
|
||||
@ -345,12 +293,23 @@ fn main() {
|
||||
item: ItemDetail::Mag(
|
||||
item::mag::Mag::baby_mag(5)
|
||||
),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}
|
||||
}
|
||||
).await.unwrap();
|
||||
|
||||
let monomates = futures::future::join_all((0..6).map(|_| {
|
||||
let mut entity_gateway = entity_gateway.clone();
|
||||
async move {
|
||||
entity_gateway.create_item(
|
||||
NewItemEntity {
|
||||
item: ItemDetail::Tool (
|
||||
item::tool::Tool {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
}).await.unwrap()
|
||||
}
|
||||
})).await;
|
||||
|
||||
let equipped = item::EquippedEntity {
|
||||
weapon: Some(item2_w.id),
|
||||
armor: Some(item7_a.id),
|
||||
@ -360,7 +319,7 @@ fn main() {
|
||||
};
|
||||
entity_gateway.set_character_equips(&character.id, &equipped).await.unwrap();
|
||||
|
||||
let inventory = item::InventoryEntity::new(vec![item0, item1, item2_w, item3, item4, item5_m, item6, item7_a, item8_s, item9_u0, item10_u1, item11_u2, item12_u3, item13]);
|
||||
let inventory = item::InventoryEntity::new(vec![InventoryItemEntity::from(item0), item1.into(), item2_w.into(), item3.into(), item4.into(), item5_m.into(), item6.into(), item7_a.into(), item8_s.into(), item9_u0.into(), item10_u1.into(), item11_u2.into(), item12_u3.into(), item13.into(), monomates.into()]);
|
||||
entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap();
|
||||
entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), item::BankName("".into())).await.unwrap();
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ async fn send_pkt<S: SendServerPacket + Send + std::fmt::Debug>(socket: Arc<asyn
|
||||
-> Result<(), NetworkError>
|
||||
{
|
||||
let buf = pkt.as_bytes();
|
||||
//println!("sndbuf: {:?}", buf);
|
||||
trace!("[send buf] {:?}", buf);
|
||||
let cbuf = cipher.lock().await.encrypt(&buf)?;
|
||||
let mut ssock = &*socket;
|
||||
ssock.write_all(&cbuf).await?;
|
||||
@ -156,7 +156,7 @@ where
|
||||
match pkt_receiver.recv_pkts().await {
|
||||
Ok(pkts) => {
|
||||
for pkt in pkts {
|
||||
trace!("[recv from {:?}] {:?}", client_id, pkt);
|
||||
info!("[recv from {:?}] {:?}", client_id, pkt);
|
||||
server_sender.send(ClientAction::Packet(client_id, pkt)).await.unwrap();
|
||||
}
|
||||
},
|
||||
@ -194,7 +194,7 @@ where
|
||||
*cipher_out.lock().await = outc;
|
||||
}
|
||||
ServerStateAction::Packet(pkt) => {
|
||||
trace!("[send to {:?}] {:?}", client_id, pkt);
|
||||
info!("[send to {:?}] {:?}", client_id, pkt);
|
||||
if let Err(err) = send_pkt(socket.clone(), cipher_out.clone(), pkt).await {
|
||||
warn!("[client {:?} send error ] {:?}", client_id, err);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use libpso::PacketParseError;
|
||||
use libpso::crypto::PSOCipher;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)]
|
||||
pub struct ClientId(pub usize);
|
||||
|
||||
pub enum OnConnect<S: SendServerPacket> {
|
||||
|
@ -5,7 +5,7 @@ use libpso::character::guildcard;
|
||||
pub const USERFLAG_NEWCHAR: u32 = 0x00000001;
|
||||
pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
|
||||
pub struct UserAccountId(pub u32);
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct UserSettingsId(pub u32);
|
||||
@ -59,6 +59,26 @@ pub struct UserAccountEntity {
|
||||
pub at_ship: bool,
|
||||
}
|
||||
|
||||
impl Default for UserAccountEntity {
|
||||
fn default() -> UserAccountEntity {
|
||||
UserAccountEntity {
|
||||
id: UserAccountId(0),
|
||||
username: "".into(),
|
||||
password: "".into(),
|
||||
guildcard: 0xFFFFFFFF,
|
||||
team_id: None,
|
||||
banned_until: None,
|
||||
muted_until: None,
|
||||
created_at: chrono::Utc::now(),
|
||||
flags: 0,
|
||||
activated: false,
|
||||
at_login: false,
|
||||
at_character: false,
|
||||
at_ship: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserAccountEntity {
|
||||
pub fn is_currently_online(&self) -> bool {
|
||||
self.at_login | self.at_character | self.at_ship
|
||||
|
@ -7,8 +7,9 @@ use libpso::character::character::{DEFAULT_PALETTE_CONFIG, DEFAULT_TECH_MENU};
|
||||
use crate::entity::item::tech::Technique;
|
||||
use crate::entity::account::UserAccountId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, enum_utils::FromStr, derive_more::Display, Serialize, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, enum_utils::FromStr, derive_more::Display, Serialize, Deserialize, Default)]
|
||||
pub enum CharacterClass {
|
||||
#[default]
|
||||
HUmar,
|
||||
HUnewearl,
|
||||
HUcast,
|
||||
@ -90,8 +91,9 @@ impl CharacterClass {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, enum_utils::FromStr, derive_more::Display, Serialize, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, enum_utils::FromStr, derive_more::Display, Serialize, Deserialize, Default)]
|
||||
pub enum SectionID {
|
||||
#[default]
|
||||
Viridia,
|
||||
Greenill,
|
||||
Skyly,
|
||||
@ -158,18 +160,12 @@ pub struct CharacterAppearance {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TechLevel(pub u8);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct CharacterTechniques {
|
||||
pub techs: HashMap<Technique, TechLevel>
|
||||
}
|
||||
|
||||
impl CharacterTechniques {
|
||||
fn new() -> CharacterTechniques {
|
||||
CharacterTechniques {
|
||||
techs: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_tech(&mut self, tech: Technique, level: TechLevel) {
|
||||
self.techs.insert(tech, TechLevel(level.0 - 1));
|
||||
}
|
||||
@ -187,18 +183,20 @@ impl CharacterTechniques {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CharacterConfig {
|
||||
pub raw_data: [u8; 0xE8],
|
||||
}
|
||||
|
||||
impl CharacterConfig {
|
||||
fn new() -> CharacterConfig {
|
||||
impl Default for CharacterConfig {
|
||||
fn default() -> CharacterConfig {
|
||||
CharacterConfig {
|
||||
raw_data: DEFAULT_PALETTE_CONFIG,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CharacterConfig {
|
||||
pub fn update(&mut self, new_config: &UpdateConfig) {
|
||||
self.raw_data = new_config.config;
|
||||
}
|
||||
@ -208,18 +206,20 @@ impl CharacterConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CharacterInfoboard {
|
||||
pub board: [u16; 172],
|
||||
}
|
||||
|
||||
impl CharacterInfoboard {
|
||||
fn new() -> CharacterInfoboard {
|
||||
impl Default for CharacterInfoboard {
|
||||
fn default() -> CharacterInfoboard {
|
||||
CharacterInfoboard {
|
||||
board: [0; 172]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CharacterInfoboard {
|
||||
pub fn as_bytes(&self) -> [u16; 172] {
|
||||
self.board
|
||||
}
|
||||
@ -229,29 +229,31 @@ impl CharacterInfoboard {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CharacterGuildCard {
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CharacterTechMenu {
|
||||
pub tech_menu: [u8; 40],
|
||||
}
|
||||
|
||||
impl CharacterTechMenu {
|
||||
fn new() -> CharacterTechMenu {
|
||||
impl Default for CharacterTechMenu {
|
||||
fn default() -> CharacterTechMenu {
|
||||
CharacterTechMenu {
|
||||
tech_menu: DEFAULT_TECH_MENU,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CharacterTechMenu {
|
||||
pub fn as_bytes(&self) -> [u8; 40] {
|
||||
self.tech_menu
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct CharacterMaterials {
|
||||
pub power: u32,
|
||||
pub mind: u32,
|
||||
@ -262,7 +264,7 @@ pub struct CharacterMaterials {
|
||||
pub tp: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default, derive_more::Display)]
|
||||
pub struct CharacterEntityId(pub u32);
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -284,8 +286,6 @@ pub struct NewCharacterEntity {
|
||||
pub materials: CharacterMaterials,
|
||||
|
||||
pub tech_menu: CharacterTechMenu,
|
||||
pub meseta: u32,
|
||||
pub bank_meseta: u32,
|
||||
pub option_flags: u32,
|
||||
}
|
||||
|
||||
@ -299,20 +299,18 @@ impl NewCharacterEntity {
|
||||
char_class: CharacterClass::HUmar,
|
||||
section_id: SectionID::Viridia,
|
||||
appearance: CharacterAppearance::default(),
|
||||
techs: CharacterTechniques::new(),
|
||||
config: CharacterConfig::new(),
|
||||
info_board: CharacterInfoboard::new(),
|
||||
techs: CharacterTechniques::default(),
|
||||
config: CharacterConfig::default(),
|
||||
info_board: CharacterInfoboard::default(),
|
||||
guildcard: CharacterGuildCard::default(),
|
||||
materials: CharacterMaterials::default(),
|
||||
tech_menu: CharacterTechMenu::new(),
|
||||
meseta: 0,
|
||||
bank_meseta: 0,
|
||||
tech_menu: CharacterTechMenu::default(),
|
||||
option_flags: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct CharacterEntity {
|
||||
pub id: CharacterEntityId,
|
||||
pub user_id: UserAccountId,
|
||||
@ -332,8 +330,5 @@ pub struct CharacterEntity {
|
||||
pub materials: CharacterMaterials,
|
||||
|
||||
pub tech_menu: CharacterTechMenu,
|
||||
pub meseta: u32,
|
||||
// TODO: this should not be tied to the character
|
||||
pub bank_meseta: u32,
|
||||
pub option_flags: u32,
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ pub trait EntityGateway: Send + Sync + Clone {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
async fn change_item_location(&mut self, _item_id: &ItemEntityId, _item_location: ItemLocation) -> Result<(), GatewayError> {
|
||||
async fn add_item_note(&mut self, _item_id: &ItemEntityId, _item_note: ItemNote) -> Result<(), GatewayError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
@ -115,4 +115,20 @@ pub trait EntityGateway: Send + Sync + Clone {
|
||||
async fn set_character_equips(&mut self, _char_id: &CharacterEntityId, _equips: &EquippedEntity) -> Result<(), GatewayError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
async fn get_character_meseta(&mut self, _char_id: &CharacterEntityId) -> Result<Meseta, GatewayError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
async fn set_character_meseta(&mut self, _char_id: &CharacterEntityId, _amount: Meseta) -> Result<(), GatewayError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
async fn get_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: BankName) -> Result<Meseta, GatewayError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: BankName, _amount: Meseta) -> Result<(), GatewayError> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ pub struct InMemoryGateway {
|
||||
users: Arc<Mutex<BTreeMap<UserAccountId, UserAccountEntity>>>,
|
||||
user_settings: Arc<Mutex<BTreeMap<UserSettingsId, UserSettingsEntity>>>,
|
||||
characters: Arc<Mutex<BTreeMap<CharacterEntityId, CharacterEntity>>>,
|
||||
character_meseta: Arc<Mutex<BTreeMap<CharacterEntityId, Meseta>>>,
|
||||
bank_meseta: Arc<Mutex<BTreeMap<(CharacterEntityId, BankName), Meseta>>>,
|
||||
items: Arc<Mutex<BTreeMap<ItemEntityId, ItemEntity>>>,
|
||||
inventories: Arc<Mutex<BTreeMap<CharacterEntityId, InventoryEntity>>>,
|
||||
banks: Arc<Mutex<BTreeMap<CharacterEntityId, BankEntity>>>,
|
||||
@ -27,6 +29,8 @@ impl Default for InMemoryGateway {
|
||||
users: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
user_settings: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
characters: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
character_meseta: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
bank_meseta: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
items: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
inventories: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
banks: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
@ -197,8 +201,6 @@ impl EntityGateway for InMemoryGateway {
|
||||
guildcard: character.guildcard,
|
||||
materials: character.materials,
|
||||
tech_menu: character.tech_menu,
|
||||
meseta: character.meseta,
|
||||
bank_meseta: character.bank_meseta,
|
||||
option_flags: character.option_flags,
|
||||
};
|
||||
characters.insert(new_character.id, new_character.clone());
|
||||
@ -223,17 +225,13 @@ impl EntityGateway for InMemoryGateway {
|
||||
+ 1;
|
||||
let new_item = ItemEntity {
|
||||
id: ItemEntityId(id),
|
||||
location: item.location,
|
||||
item: item.item,
|
||||
};
|
||||
items.insert(ItemEntityId(id), new_item.clone());
|
||||
Ok(new_item)
|
||||
}
|
||||
|
||||
async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> {
|
||||
if let Some(item_entity) = self.items.lock().unwrap().get_mut(item_id) {
|
||||
item_entity.location = item_location
|
||||
}
|
||||
async fn add_item_note(&mut self, _item_id: &ItemEntityId, _item_note: ItemNote) -> Result<(), GatewayError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -272,7 +270,6 @@ impl EntityGateway for InMemoryGateway {
|
||||
}
|
||||
|
||||
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
|
||||
println!("getting inv");
|
||||
let inventories = self.inventories.lock().unwrap();
|
||||
Ok(inventories
|
||||
.iter()
|
||||
@ -318,4 +315,36 @@ impl EntityGateway for InMemoryGateway {
|
||||
equips.insert(*char_id, equipped.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> {
|
||||
let mut character_meseta = self.character_meseta.lock().unwrap();
|
||||
character_meseta.insert(*char_id, meseta);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result<Meseta, GatewayError> {
|
||||
let mut character_meseta = self.character_meseta.lock().unwrap();
|
||||
if let Some(meseta) = character_meseta.get_mut(char_id) {
|
||||
Ok(*meseta)
|
||||
}
|
||||
else {
|
||||
Err(GatewayError::Error)
|
||||
}
|
||||
}
|
||||
|
||||
async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> {
|
||||
let mut bank_meseta = self.bank_meseta.lock().unwrap();
|
||||
bank_meseta.insert((*char_id, bank), meseta);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName) -> Result<Meseta, GatewayError> {
|
||||
let mut bank_meseta = self.bank_meseta.lock().unwrap();
|
||||
if let Some(meseta) = bank_meseta.get_mut(&(*char_id, bank)) {
|
||||
Ok(*meseta)
|
||||
}
|
||||
else {
|
||||
Err(GatewayError::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
drop table item_location;
|
||||
|
||||
create table item_note (
|
||||
item integer references item (id) not null,
|
||||
note jsonb not null,
|
||||
created_at timestamptz default current_timestamp not null
|
||||
);
|
15
src/entity/gateway/postgres/migrations/V0004__meseta.sql
Normal file
15
src/entity/gateway/postgres/migrations/V0004__meseta.sql
Normal file
@ -0,0 +1,15 @@
|
||||
create table character_meseta (
|
||||
pchar integer references character (id) not null unique,
|
||||
meseta integer not null,
|
||||
);
|
||||
|
||||
create table bank_meseta (
|
||||
pchar integer references character (id) not null,
|
||||
bank varchar(128) not null,
|
||||
meseta integer not null,
|
||||
unique (pchar, bank)
|
||||
);
|
||||
|
||||
|
||||
alter table player_character
|
||||
drop column meseta, bank_meseta;
|
@ -1,3 +1,4 @@
|
||||
#![allow(dead_code)]
|
||||
use std::collections::HashMap;
|
||||
use std::convert::Into;
|
||||
use serde::{Serialize, Deserialize};
|
||||
@ -216,8 +217,6 @@ pub struct PgCharacter {
|
||||
tp: i16,
|
||||
|
||||
tech_menu: Vec<u8>,
|
||||
meseta: i32,
|
||||
bank_meseta: i32,
|
||||
}
|
||||
|
||||
impl From<PgCharacter> for CharacterEntity {
|
||||
@ -267,8 +266,6 @@ impl From<PgCharacter> for CharacterEntity {
|
||||
tech_menu: CharacterTechMenu {
|
||||
tech_menu: vec_to_array(other.tech_menu)
|
||||
},
|
||||
meseta: other.meseta as u32,
|
||||
bank_meseta: other.bank_meseta as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -571,22 +568,21 @@ pub struct PgItem {
|
||||
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum PgItemLocationDetail {
|
||||
Inventory {
|
||||
pub enum PgItemNoteDetail {
|
||||
CharacterCreation {
|
||||
character_id: u32,
|
||||
},
|
||||
Bank {
|
||||
character_id: u32,
|
||||
name: String,
|
||||
},
|
||||
LocalFloor {
|
||||
EnemyDrop {
|
||||
character_id: u32,
|
||||
map_area: MapArea,
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
},
|
||||
SharedFloor {
|
||||
Pickup {
|
||||
character_id: u32,
|
||||
},
|
||||
PlayerDrop {
|
||||
map_area: MapArea,
|
||||
x: f32,
|
||||
y: f32,
|
||||
@ -596,42 +592,92 @@ pub enum PgItemLocationDetail {
|
||||
FedToMag {
|
||||
mag: u32,
|
||||
},
|
||||
Shop,
|
||||
BoughtAtShop {
|
||||
character_id: u32,
|
||||
},
|
||||
SoldToShop,
|
||||
Trade {
|
||||
id: u32,
|
||||
character_to: u32,
|
||||
character_from: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ItemLocation> for PgItemLocationDetail {
|
||||
fn from(other: ItemLocation) -> PgItemLocationDetail {
|
||||
impl From<ItemNote> for PgItemNoteDetail {
|
||||
fn from(other: ItemNote) -> PgItemNoteDetail {
|
||||
match other {
|
||||
ItemLocation::Inventory{character_id} => PgItemLocationDetail::Inventory{character_id: character_id.0},
|
||||
ItemLocation::Bank{character_id, name} => PgItemLocationDetail::Bank{character_id: character_id.0, name: name.0},
|
||||
ItemLocation::LocalFloor{character_id, map_area, x,y,z} => PgItemLocationDetail::LocalFloor{character_id: character_id.0, map_area, x,y,z},
|
||||
ItemLocation::SharedFloor{map_area, x,y,z} => PgItemLocationDetail::SharedFloor{map_area, x,y,z},
|
||||
ItemLocation::Consumed => PgItemLocationDetail::Consumed,
|
||||
ItemLocation::FedToMag{mag} => PgItemLocationDetail::FedToMag{mag: mag.0},
|
||||
ItemLocation::Shop => PgItemLocationDetail::Shop,
|
||||
ItemNote::CharacterCreation{character_id} => PgItemNoteDetail::CharacterCreation {
|
||||
character_id: character_id.0,
|
||||
},
|
||||
ItemNote::EnemyDrop{character_id, map_area, x, y, z} => PgItemNoteDetail::EnemyDrop {
|
||||
character_id: character_id.0,
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
ItemNote::Pickup{character_id} => PgItemNoteDetail::Pickup {
|
||||
character_id: character_id.0,
|
||||
},
|
||||
ItemNote::PlayerDrop{map_area, x, y, z} => PgItemNoteDetail::PlayerDrop {
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
ItemNote::Consumed => PgItemNoteDetail::Consumed,
|
||||
ItemNote::FedToMag{mag} => PgItemNoteDetail::FedToMag{
|
||||
mag: mag.0
|
||||
},
|
||||
ItemNote::BoughtAtShop{character_id} => PgItemNoteDetail::BoughtAtShop {
|
||||
character_id: character_id.0,
|
||||
},
|
||||
ItemNote::SoldToShop => PgItemNoteDetail::SoldToShop,
|
||||
ItemNote::Trade{id, character_to, character_from} => PgItemNoteDetail::Trade {
|
||||
id: id.0,
|
||||
character_to: character_to.0,
|
||||
character_from: character_from.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PgItemLocationDetail> for ItemLocation {
|
||||
fn from(other: PgItemLocationDetail) -> ItemLocation {
|
||||
match other{
|
||||
PgItemLocationDetail::Inventory{character_id} => ItemLocation::Inventory{character_id: CharacterEntityId(character_id)},
|
||||
PgItemLocationDetail::Bank{character_id, name} => ItemLocation::Bank{character_id: CharacterEntityId(character_id), name: BankName(name)},
|
||||
PgItemLocationDetail::LocalFloor{character_id, map_area, x,y,z} => ItemLocation::LocalFloor{character_id: CharacterEntityId(character_id), map_area, x,y,z},
|
||||
PgItemLocationDetail::SharedFloor{map_area, x,y,z} => ItemLocation::SharedFloor{map_area, x,y,z},
|
||||
PgItemLocationDetail::Consumed => ItemLocation::Consumed,
|
||||
PgItemLocationDetail::FedToMag{mag} => ItemLocation::FedToMag{mag: ItemEntityId(mag)},
|
||||
PgItemLocationDetail::Shop => ItemLocation::Shop,
|
||||
impl From<PgItemNoteDetail> for ItemNote {
|
||||
fn from(other: PgItemNoteDetail) -> ItemNote {
|
||||
match other {
|
||||
PgItemNoteDetail::CharacterCreation{character_id} => ItemNote::CharacterCreation {
|
||||
character_id: CharacterEntityId(character_id as u32),
|
||||
},
|
||||
PgItemNoteDetail::EnemyDrop{character_id, map_area, x, y, z} => ItemNote::EnemyDrop {
|
||||
character_id: CharacterEntityId(character_id as u32),
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
PgItemNoteDetail::Pickup{character_id} => ItemNote::Pickup {
|
||||
character_id: CharacterEntityId(character_id as u32),
|
||||
},
|
||||
PgItemNoteDetail::PlayerDrop{map_area, x, y, z} => ItemNote::PlayerDrop {
|
||||
map_area,
|
||||
x,y,z,
|
||||
},
|
||||
PgItemNoteDetail::Consumed => ItemNote::Consumed,
|
||||
PgItemNoteDetail::FedToMag{mag} => ItemNote::FedToMag{
|
||||
mag: ItemEntityId(mag)
|
||||
},
|
||||
PgItemNoteDetail::BoughtAtShop{character_id} => ItemNote::BoughtAtShop {
|
||||
character_id: CharacterEntityId(character_id),
|
||||
},
|
||||
PgItemNoteDetail::SoldToShop => ItemNote::SoldToShop,
|
||||
PgItemNoteDetail::Trade {id, character_to, character_from} => ItemNote::Trade {
|
||||
id: TradeId(id as u32),
|
||||
character_to: CharacterEntityId(character_to as u32),
|
||||
character_from: CharacterEntityId(character_from as u32),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
pub struct PgItemLocation {
|
||||
pub struct PgItemNote {
|
||||
//pub id: i32,
|
||||
pub location: sqlx::types::Json<PgItemLocationDetail>,
|
||||
pub note: sqlx::types::Json<PgItemNoteDetail>,
|
||||
created_at: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
@ -680,19 +726,20 @@ pub struct PgItemEntity {
|
||||
pub item: sqlx::types::Json<PgItemDetail>,
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
pub struct PgItemWithLocation {
|
||||
pub id: i32,
|
||||
pub item: sqlx::types::Json<PgItemDetail>,
|
||||
pub location: sqlx::types::Json<PgItemLocationDetail>,
|
||||
}
|
||||
*/
|
||||
|
||||
impl From<PgItemWithLocation> for ItemEntity {
|
||||
fn from(other: PgItemWithLocation) -> ItemEntity {
|
||||
impl From<PgItemEntity> for ItemEntity {
|
||||
fn from(other: PgItemEntity) -> ItemEntity {
|
||||
ItemEntity {
|
||||
id: ItemEntityId(other.id as u32),
|
||||
item: other.item.0.into(),
|
||||
location: other.location.0.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ impl PostgresGateway {
|
||||
}
|
||||
|
||||
async fn apply_item_modifications(&self, item: ItemEntity) -> ItemEntity {
|
||||
let ItemEntity {id, item, location} = item;
|
||||
let ItemEntity {id, item} = item;
|
||||
|
||||
let item = match item {
|
||||
ItemDetail::Weapon(mut weapon) => {
|
||||
@ -101,7 +101,6 @@ impl PostgresGateway {
|
||||
ItemEntity {
|
||||
id,
|
||||
item,
|
||||
location
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -183,7 +182,7 @@ impl EntityGateway for PostgresGateway {
|
||||
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
||||
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,
|
||||
config, infoboard, guildcard, power, mind, def, evade, luck, hp, tp, tech_menu, meseta, bank_meseta, option_flags)
|
||||
config, infoboard, guildcard, power, mind, def, evade, luck, hp, tp, tech_menu, option_flags)
|
||||
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)
|
||||
returning *;"#;
|
||||
@ -216,8 +215,6 @@ impl EntityGateway for PostgresGateway {
|
||||
.bind(char.materials.hp as i16)
|
||||
.bind(char.materials.tp as i16)
|
||||
.bind(char.tech_menu.tech_menu.to_vec())
|
||||
.bind(char.meseta as i32)
|
||||
.bind(char.bank_meseta as i32)
|
||||
.bind(char.option_flags as i32)
|
||||
.fetch_one(&self.pool).await?;
|
||||
|
||||
@ -242,7 +239,7 @@ impl EntityGateway for PostgresGateway {
|
||||
let q = r#"update player_character set
|
||||
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,
|
||||
evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, meseta=$29, bank_meseta=$30, option_flags=$31
|
||||
evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29
|
||||
where id=$32;"#;
|
||||
sqlx::query(q)
|
||||
.bind(char.user_id.0)
|
||||
@ -273,8 +270,6 @@ impl EntityGateway for PostgresGateway {
|
||||
.bind(char.materials.hp as i16)
|
||||
.bind(char.materials.tp as i16)
|
||||
.bind(char.tech_menu.tech_menu.to_vec())
|
||||
.bind(char.meseta as i32)
|
||||
.bind(char.bank_meseta as i32)
|
||||
.bind(char.option_flags as i32)
|
||||
.bind(char.id.0 as i32)
|
||||
.execute(&self.pool).await?;
|
||||
@ -294,64 +289,18 @@ impl EntityGateway for PostgresGateway {
|
||||
let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;")
|
||||
.bind(sqlx::types::Json(PgItemDetail::from(item.item)))
|
||||
.fetch_one(&mut tx).await?;
|
||||
let location = sqlx::query_as::<_, PgItemLocation>("insert into item_location (item, location) values ($1, $2) returning *")
|
||||
.bind(new_item.id)
|
||||
.bind(sqlx::types::Json(PgItemLocationDetail::from(item.location)))
|
||||
.fetch_one(&mut tx).await?;
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(ItemEntity {
|
||||
id: ItemEntityId(new_item.id as u32),
|
||||
item: new_item.item.0.into(),
|
||||
location: location.location.0.into(),
|
||||
})
|
||||
|
||||
/*
|
||||
let mut tx = self.pool.begin().await?;
|
||||
let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;")
|
||||
.bind(sqlx::types::Json(PgItemDetail::from(item.item)))
|
||||
.fetch_one(&mut tx).await?;
|
||||
let location = if let ItemLocation::Inventory{slot, ..} = &item.location {
|
||||
sqlx::query("insert into item_location (item, location) values ($1, $2)")
|
||||
.bind(new_item.id)
|
||||
.bind(sqlx::types::Json(PgItemLocationDetail::from(item.location.clone())))
|
||||
.execute(&mut tx).await?;
|
||||
sqlx::query("insert into inventory_slot (item, slot) values ($1, $2)")
|
||||
.bind(new_item.id)
|
||||
.bind(*slot as i32)
|
||||
.execute(&mut tx).await?;
|
||||
sqlx::query_as::<_, PgItemLocation>(r#"select
|
||||
item_location.item,
|
||||
jsonb_set(item_location.location, '{Inventory,slot}', inventory_slot.slot::text::jsonb) as location,
|
||||
item_location.created_at
|
||||
from item_location
|
||||
join item on item.id = item_location.item
|
||||
join inventory_slot on inventory_slot.item = item.id
|
||||
where item.id = $1
|
||||
order by item_location.created_at
|
||||
limit 1"#)
|
||||
.bind(new_item.id)
|
||||
.fetch_one(&mut tx).await?
|
||||
}
|
||||
else {
|
||||
sqlx::query_as::<_, PgItemLocation>("insert into item_location (item, location) values ($1, $2) returning *")
|
||||
.bind(new_item.id)
|
||||
.bind(sqlx::types::Json(PgItemLocationDetail::from(item.location)))
|
||||
.fetch_one(&mut tx).await?
|
||||
};
|
||||
tx.commit().await?;
|
||||
Ok(ItemEntity {
|
||||
id: ItemEntityId(new_item.id as u32),
|
||||
item: new_item.item.0.into(),
|
||||
location: location.location.0.into(),
|
||||
})
|
||||
*/
|
||||
}
|
||||
|
||||
async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> {
|
||||
sqlx::query("insert into item_location (item, location) values ($1, $2)")
|
||||
async fn add_item_note(&mut self, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> {
|
||||
sqlx::query("insert into item_note(item, note) values ($1, $2)")
|
||||
.bind(item_id.0)
|
||||
.bind(sqlx::types::Json(PgItemLocationDetail::from(item_location)))
|
||||
.bind(sqlx::types::Json(PgItemNoteDetail::from(item_note)))
|
||||
.execute(&self.pool).await?;
|
||||
Ok(())
|
||||
|
||||
@ -465,7 +414,7 @@ impl EntityGateway for PostgresGateway {
|
||||
for inv_item in inventory.items.0.into_iter() {
|
||||
match inv_item {
|
||||
PgInventoryItemEntity::Individual(item) => {
|
||||
let entity = sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
|
||||
let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1")
|
||||
.bind(item)
|
||||
.fetch_one(&self.pool).await
|
||||
.map(|item| item.into())
|
||||
@ -476,7 +425,7 @@ impl EntityGateway for PostgresGateway {
|
||||
PgInventoryItemEntity::Stacked(items) => {
|
||||
let mut stacked_item = Vec::new();
|
||||
for s_item in items {
|
||||
stacked_item.push(sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
|
||||
stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1")
|
||||
.bind(s_item)
|
||||
.fetch_one(&self.pool).await
|
||||
.map(|item| item.into())
|
||||
@ -501,7 +450,7 @@ impl EntityGateway for PostgresGateway {
|
||||
for bank_item in bank.items.0.into_iter() {
|
||||
match bank_item {
|
||||
PgInventoryItemEntity::Individual(item) => {
|
||||
let entity = sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
|
||||
let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1")
|
||||
.bind(item)
|
||||
.fetch_one(&self.pool).await
|
||||
.map(|item| item.into())
|
||||
@ -512,7 +461,7 @@ impl EntityGateway for PostgresGateway {
|
||||
PgInventoryItemEntity::Stacked(items) => {
|
||||
let mut stacked_item = Vec::new();
|
||||
for s_item in items {
|
||||
stacked_item.push(sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
|
||||
stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1")
|
||||
.bind(s_item)
|
||||
.fetch_one(&self.pool).await
|
||||
.map(|item| item.into())
|
||||
@ -597,4 +546,44 @@ impl EntityGateway for PostgresGateway {
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> {
|
||||
sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set items = $2")
|
||||
.bind(char_id.0)
|
||||
.bind(meseta.0 as i32)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result<Meseta, GatewayError> {
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct PgMeseta(i32);
|
||||
let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1"#)
|
||||
.bind(char_id.0)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(Meseta(meseta.0 as u32))
|
||||
}
|
||||
|
||||
async fn set_bank_meseta(&mut self, 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")
|
||||
.bind(char_id.0)
|
||||
.bind(meseta.0 as i32)
|
||||
.bind(bank.0)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName) -> Result<Meseta, GatewayError> {
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct PgMeseta(i32);
|
||||
let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1 and bank = $2"#)
|
||||
.bind(char_id.0)
|
||||
.bind(bank.0)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(Meseta(meseta.0 as u32))
|
||||
}
|
||||
}
|
||||
|
@ -567,6 +567,7 @@ impl Mag {
|
||||
pub fn as_bytes(&self) -> [u8; 16] {
|
||||
let mut result = [0; 16];
|
||||
result[0..3].copy_from_slice(&self.mag.value());
|
||||
result[2] = self.level() as u8;
|
||||
result[3] = self.photon_blast_value();
|
||||
result[4..6].copy_from_slice(&self.def.to_le_bytes());
|
||||
result[6..8].copy_from_slice(&self.pow.to_le_bytes());
|
||||
|
@ -17,26 +17,29 @@ use crate::ship::drops::ItemDropType;
|
||||
pub struct ItemEntityId(pub u32);
|
||||
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
|
||||
pub struct ItemId(u32);
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, derive_more::Display)]
|
||||
pub struct BankName(pub String);
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct TradeId(pub u32);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ItemLocation {
|
||||
Inventory {
|
||||
pub enum ItemNote {
|
||||
CharacterCreation {
|
||||
character_id: CharacterEntityId,
|
||||
},
|
||||
Bank {
|
||||
character_id: CharacterEntityId,
|
||||
name: BankName,
|
||||
},
|
||||
LocalFloor {
|
||||
EnemyDrop {
|
||||
character_id: CharacterEntityId,
|
||||
//monster_type: MonsterType,
|
||||
//droprate: f32,
|
||||
map_area: MapArea,
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
},
|
||||
SharedFloor {
|
||||
Pickup {
|
||||
character_id: CharacterEntityId,
|
||||
},
|
||||
PlayerDrop {
|
||||
map_area: MapArea,
|
||||
x: f32,
|
||||
y: f32,
|
||||
@ -46,18 +49,18 @@ pub enum ItemLocation {
|
||||
FedToMag {
|
||||
mag: ItemEntityId,
|
||||
},
|
||||
Shop,
|
||||
/*Destroyed {
|
||||
// marks an item that has been consumed in some way
|
||||
BoughtAtShop {
|
||||
character_id: CharacterEntityId,
|
||||
},
|
||||
SoldToShop,
|
||||
Trade {
|
||||
id: TradeId,
|
||||
character_to: CharacterEntityId,
|
||||
character_from: CharacterEntityId,
|
||||
},
|
||||
Transformed {
|
||||
item_id,
|
||||
change_event
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Meseta(pub u32);
|
||||
|
||||
impl Meseta {
|
||||
@ -159,18 +162,23 @@ impl ItemDetail {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tool(&self) -> Option<&tool::Tool> {
|
||||
match self {
|
||||
ItemDetail::Tool(tool) => Some(tool),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NewItemEntity {
|
||||
pub location: ItemLocation,
|
||||
pub item: ItemDetail,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ItemEntity {
|
||||
pub id: ItemEntityId,
|
||||
pub location: ItemLocation,
|
||||
pub item: ItemDetail,
|
||||
}
|
||||
|
||||
@ -215,6 +223,13 @@ impl InventoryItemEntity {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn individual(&self) -> Option<&ItemEntity> {
|
||||
match self {
|
||||
InventoryItemEntity::Individual(i) => Some(i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -2,7 +2,8 @@
|
||||
#![feature(maybe_uninit_extra)]
|
||||
#![feature(inline_const)]
|
||||
#![feature(drain_filter)]
|
||||
|
||||
#![feature(derive_default_enum)]
|
||||
#![feature(try_blocks)]
|
||||
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@ use libpso::{utf8_to_array, utf8_to_utf16_array};
|
||||
|
||||
use crate::entity::gateway::{EntityGateway, GatewayError};
|
||||
use crate::entity::account::{UserAccountId, UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
|
||||
use crate::entity::item::{NewItemEntity, ItemDetail, ItemLocation, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity};
|
||||
use crate::entity::item::{NewItemEntity, ItemDetail, ItemNote, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity, Meseta};
|
||||
use crate::entity::item::weapon::Weapon;
|
||||
use crate::entity::item::armor::Armor;
|
||||
use crate::entity::item::tech::Technique;
|
||||
@ -201,8 +201,8 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
|
||||
_ => {}
|
||||
}
|
||||
|
||||
character.meseta = 300;
|
||||
let character = entity_gateway.create_character(character).await?;
|
||||
entity_gateway.set_character_meseta(&character.id, Meseta(300)).await?;
|
||||
|
||||
let new_weapon = match character.char_class {
|
||||
CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber,
|
||||
@ -220,10 +220,11 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
|
||||
special: None,
|
||||
attrs: [None; 3],
|
||||
tekked: true,
|
||||
}),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}}).await?;
|
||||
})}).await?;
|
||||
|
||||
entity_gateway.add_item_note(&weapon.id, ItemNote::CharacterCreation {
|
||||
character_id: character.id,
|
||||
}).await?;
|
||||
|
||||
let armor = entity_gateway.create_item(
|
||||
NewItemEntity {
|
||||
@ -233,10 +234,11 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
|
||||
dfp: 0,
|
||||
evp: 0,
|
||||
slots: 0,
|
||||
}),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}}).await?;
|
||||
})}).await?;
|
||||
|
||||
entity_gateway.add_item_note(&armor.id, ItemNote::CharacterCreation {
|
||||
character_id: character.id,
|
||||
}).await?;
|
||||
|
||||
let mut mag = {
|
||||
if character.char_class.is_android() {
|
||||
@ -249,35 +251,40 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
|
||||
let mag = entity_gateway.create_item(
|
||||
NewItemEntity {
|
||||
item: ItemDetail::Mag(mag),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}}).await?;
|
||||
}).await?;
|
||||
|
||||
let mut monomates = Vec::new();
|
||||
for _ in 0..4usize {
|
||||
monomates.push(entity_gateway.create_item(
|
||||
NewItemEntity {
|
||||
item: ItemDetail::Tool (
|
||||
Tool {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}}).await?)
|
||||
}
|
||||
entity_gateway.add_item_note(&mag.id, ItemNote::CharacterCreation {
|
||||
character_id: character.id,
|
||||
}).await?;
|
||||
|
||||
let mut monofluids = Vec::new();
|
||||
for _ in 0..4usize {
|
||||
monofluids.push(entity_gateway.create_item(
|
||||
NewItemEntity {
|
||||
item: ItemDetail::Tool (
|
||||
Tool {
|
||||
tool: item::tool::ToolType::Monofluid,
|
||||
}),
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: character.id,
|
||||
}}).await?)
|
||||
}
|
||||
let (monomates, monofluids) = futures::future::join_all((0..4usize).map(|_| {
|
||||
let mut eg = entity_gateway.clone();
|
||||
let character_id = character.id;
|
||||
async move {
|
||||
let monomate = eg.create_item(
|
||||
NewItemEntity {
|
||||
item: ItemDetail::Tool (
|
||||
Tool {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
})}).await?;
|
||||
|
||||
eg.add_item_note(&monomate.id, ItemNote::CharacterCreation {
|
||||
character_id
|
||||
}).await?;
|
||||
|
||||
let monofluid = eg.create_item(
|
||||
NewItemEntity {
|
||||
item: ItemDetail::Tool (
|
||||
Tool {
|
||||
tool: item::tool::ToolType::Monofluid,
|
||||
})}).await?;
|
||||
|
||||
eg.add_item_note(&monofluid.id, ItemNote::CharacterCreation {
|
||||
character_id
|
||||
}).await?;
|
||||
|
||||
Ok((monomate, monofluid))
|
||||
}})).await.into_iter().collect::<Result<Vec<_>, GatewayError>>()?.into_iter().unzip();
|
||||
|
||||
let inventory = InventoryEntity {
|
||||
items: vec![InventoryItemEntity::Individual(weapon.clone()), InventoryItemEntity::Individual(armor.clone()), InventoryItemEntity::Individual(mag.clone()),
|
||||
|
@ -2,24 +2,17 @@ use libpso::character::character;
|
||||
use crate::common::leveltable::CharacterStats;
|
||||
use crate::entity::character::CharacterEntity;
|
||||
use crate::ship::items::{CharacterInventory, CharacterBank};
|
||||
use crate::entity::item::Meseta;
|
||||
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CharacterBytesBuilder<'a> {
|
||||
character: Option<&'a CharacterEntity>,
|
||||
stats: Option<&'a CharacterStats>,
|
||||
level: Option<u32>,
|
||||
meseta: Option<Meseta>,
|
||||
}
|
||||
|
||||
impl<'a> Default for CharacterBytesBuilder<'a> {
|
||||
fn default() -> CharacterBytesBuilder<'a> {
|
||||
CharacterBytesBuilder {
|
||||
character: None,
|
||||
stats: None,
|
||||
level: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> CharacterBytesBuilder<'a> {
|
||||
pub fn character(self, character: &'a CharacterEntity) -> CharacterBytesBuilder<'a> {
|
||||
CharacterBytesBuilder {
|
||||
@ -42,10 +35,18 @@ impl<'a> CharacterBytesBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meseta(self, meseta: Meseta) -> CharacterBytesBuilder<'a> {
|
||||
CharacterBytesBuilder {
|
||||
meseta: Some(meseta),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(self) -> character::Character {
|
||||
let character = self.character.unwrap();
|
||||
let stats = self.stats.unwrap();
|
||||
let level = self.level.unwrap();
|
||||
let meseta = self.meseta.unwrap();
|
||||
character::Character {
|
||||
name: libpso::utf8_to_utf16_array!(character.name, 16),
|
||||
hp: stats.hp,
|
||||
@ -70,7 +71,7 @@ impl<'a> CharacterBytesBuilder<'a> {
|
||||
prop_y: character.appearance.prop_y,
|
||||
config: character.config.as_bytes(),
|
||||
techniques: character.techs.as_bytes(),
|
||||
meseta: character.meseta,
|
||||
meseta: meseta.0 as u32,
|
||||
exp: character.exp,
|
||||
..character::Character::default()
|
||||
}
|
||||
@ -78,10 +79,12 @@ impl<'a> CharacterBytesBuilder<'a> {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FullCharacterBytesBuilder<'a> {
|
||||
character: Option<&'a CharacterEntity>,
|
||||
stats: Option<&'a CharacterStats>,
|
||||
level: Option<u32>,
|
||||
meseta: Option<Meseta>,
|
||||
inventory: Option<&'a CharacterInventory>,
|
||||
bank: Option<&'a CharacterBank>,
|
||||
key_config: Option<&'a [u8; 0x16C]>,
|
||||
@ -91,24 +94,6 @@ pub struct FullCharacterBytesBuilder<'a> {
|
||||
option_flags: Option<u32>,
|
||||
}
|
||||
|
||||
impl<'a> Default for FullCharacterBytesBuilder<'a> {
|
||||
fn default() -> FullCharacterBytesBuilder<'a> {
|
||||
FullCharacterBytesBuilder {
|
||||
character: None,
|
||||
stats: None,
|
||||
level: None,
|
||||
inventory: None,
|
||||
bank: None,
|
||||
key_config: None,
|
||||
joystick_config: None,
|
||||
symbol_chat: None,
|
||||
tech_menu: None,
|
||||
option_flags: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> FullCharacterBytesBuilder<'a> {
|
||||
pub fn character(self, character: &'a CharacterEntity) -> FullCharacterBytesBuilder<'a> {
|
||||
FullCharacterBytesBuilder {
|
||||
@ -131,6 +116,13 @@ impl<'a> FullCharacterBytesBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meseta(self, meseta: Meseta) -> FullCharacterBytesBuilder<'a> {
|
||||
FullCharacterBytesBuilder {
|
||||
meseta: Some(meseta),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inventory(self, inventory: &'a CharacterInventory) -> FullCharacterBytesBuilder<'a> {
|
||||
FullCharacterBytesBuilder {
|
||||
inventory: Some(inventory),
|
||||
@ -184,6 +176,7 @@ impl<'a> FullCharacterBytesBuilder<'a> {
|
||||
let character = self.character.unwrap();
|
||||
let stats = self.stats.unwrap();
|
||||
let level = self.level.unwrap();
|
||||
let meseta = self.meseta.unwrap();
|
||||
let inventory = self.inventory.unwrap();
|
||||
let bank = self.bank.unwrap();
|
||||
let key_config = self.key_config.unwrap();
|
||||
@ -204,6 +197,7 @@ impl<'a> FullCharacterBytesBuilder<'a> {
|
||||
.character(character)
|
||||
.stats(stats)
|
||||
.level(level - 1)
|
||||
.meseta(meseta)
|
||||
.build(),
|
||||
inventory: character::Inventory {
|
||||
item_count: inventory.count() as u8,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::ship::items::ClientItemId;
|
||||
use libpso::character::character;//::InventoryItem;
|
||||
use crate::entity::item::{ItemEntityId, ItemEntity, ItemDetail, ItemLocation, BankEntity, BankItemEntity, BankName};
|
||||
use crate::entity::item::{ItemEntityId, ItemEntity, ItemDetail, BankEntity, BankItemEntity, BankName};
|
||||
use crate::entity::character::CharacterEntityId;
|
||||
use crate::entity::item::tool::Tool;
|
||||
use crate::ship::items::inventory::{InventoryItemHandle, InventoryItem};
|
||||
@ -293,7 +293,7 @@ impl CharacterBank {
|
||||
self.items.last()
|
||||
}
|
||||
|
||||
pub fn as_bank_entity(&self, character_id: &CharacterEntityId, bank_name: &BankName) -> BankEntity {
|
||||
pub fn as_bank_entity(&self, _character_id: &CharacterEntityId, _bank_name: &BankName) -> BankEntity {
|
||||
BankEntity {
|
||||
items: self.items.iter()
|
||||
.map(|item| {
|
||||
@ -301,26 +301,18 @@ impl CharacterBank {
|
||||
BankItem::Individual(item) => {
|
||||
BankItemEntity::Individual(ItemEntity {
|
||||
id: item.entity_id,
|
||||
location: ItemLocation::Bank {
|
||||
character_id: *character_id,
|
||||
name: bank_name.clone(),
|
||||
},
|
||||
item: item.item.clone(),
|
||||
})
|
||||
},
|
||||
BankItem::Stacked(items) => {
|
||||
BankItemEntity::Stacked(items.entity_ids.iter()
|
||||
.map(|id| {
|
||||
ItemEntity {
|
||||
id: *id,
|
||||
location: ItemLocation::Bank {
|
||||
character_id: *character_id,
|
||||
name: bank_name.clone(),
|
||||
},
|
||||
item: ItemDetail::Tool(items.tool)
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
.map(|id| {
|
||||
ItemEntity {
|
||||
id: *id,
|
||||
item: ItemDetail::Tool(items.tool)
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
},
|
||||
}
|
||||
})
|
||||
|
@ -159,24 +159,24 @@ impl<'a> FloorItemHandle<'a> {
|
||||
}
|
||||
|
||||
// TODO: floors should keep track of their own item_ids
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RoomFloorItems(Vec<FloorItem>);
|
||||
|
||||
impl Default for RoomFloorItems {
|
||||
fn default() -> RoomFloorItems {
|
||||
RoomFloorItems(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl RoomFloorItems {
|
||||
pub fn add_item(&mut self, item: FloorItem) {
|
||||
self.0.push(item);
|
||||
}
|
||||
|
||||
pub fn remove_item(&mut self, item_id: &ClientItemId) {
|
||||
self.0.retain(|item| item.item_id() != *item_id);
|
||||
}
|
||||
|
||||
// TODO: &ClientItemId
|
||||
pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&FloorItem> {
|
||||
self.0.iter().find(|item| item.item_id() == item_id)
|
||||
}
|
||||
|
||||
// TODO: &ClientItemId
|
||||
pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option<FloorItemHandle> {
|
||||
let index = self.0.iter().position(|item| item.item_id() == item_id)?;
|
||||
Some(FloorItemHandle {
|
||||
|
@ -2,7 +2,7 @@ use std::cmp::Ordering;
|
||||
use thiserror::Error;
|
||||
use libpso::character::character;//::InventoryItem;
|
||||
use crate::entity::character::CharacterEntityId;
|
||||
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, ItemLocation, InventoryEntity, InventoryItemEntity, EquippedEntity};
|
||||
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, InventoryEntity, InventoryItemEntity, EquippedEntity};
|
||||
use crate::entity::item::tool::Tool;
|
||||
use crate::entity::item::mag::Mag;
|
||||
use crate::entity::item::weapon::Weapon;
|
||||
@ -87,6 +87,24 @@ pub enum InventoryItemAddToError {
|
||||
pub enum InventoryAddError {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum YesThereIsSpace {
|
||||
NewStack,
|
||||
ExistingStack,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NoThereIsNotSpace {
|
||||
FullStack,
|
||||
FullInventory,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SpaceForStack {
|
||||
Yes(YesThereIsSpace),
|
||||
No(NoThereIsNotSpace),
|
||||
}
|
||||
|
||||
impl InventoryItem {
|
||||
pub fn entity_ids(&self) -> Vec<ItemEntityId> {
|
||||
match self {
|
||||
@ -220,6 +238,27 @@ impl InventoryItem {
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stacked(&self) -> Option<&StackedInventoryItem> {
|
||||
match self {
|
||||
InventoryItem::Stacked(ref stacked_inventory_item) => Some(stacked_inventory_item),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stacked_mut(&mut self) -> Option<&mut StackedInventoryItem> {
|
||||
match self {
|
||||
InventoryItem::Stacked(ref mut stacked_inventory_item) => Some(stacked_inventory_item),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mag(&self) -> Option<&Mag> {
|
||||
match self {
|
||||
InventoryItem::Individual(individual_inventory_item) => individual_inventory_item.mag(),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -413,6 +452,62 @@ impl CharacterInventory {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
pub fn space_for_individual_item(&self) -> bool {
|
||||
self.count() < INVENTORY_CAPACITY
|
||||
}
|
||||
|
||||
pub fn space_for_stacked_item(&self, tool: &Tool, amount: usize) -> SpaceForStack {
|
||||
let existing_item = self.items.iter()
|
||||
.filter_map(|item| {
|
||||
match item {
|
||||
InventoryItem::Stacked(s_item) => {
|
||||
Some(s_item)
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
})
|
||||
.find(|s_item| {
|
||||
s_item.tool == *tool
|
||||
});
|
||||
|
||||
match existing_item {
|
||||
Some(item) => {
|
||||
if item.count() + amount <= tool.tool.max_stack() {
|
||||
SpaceForStack::Yes(YesThereIsSpace::ExistingStack)
|
||||
}
|
||||
else {
|
||||
SpaceForStack::No(NoThereIsNotSpace::FullStack)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if self.count() < INVENTORY_CAPACITY {
|
||||
SpaceForStack::Yes(YesThereIsSpace::NewStack)
|
||||
}
|
||||
else {
|
||||
SpaceForStack::No(NoThereIsNotSpace::FullInventory)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stack_item_id(&self, tool: &Tool) -> Option<ClientItemId> {
|
||||
self.items.iter()
|
||||
.filter_map(|item| {
|
||||
match item {
|
||||
InventoryItem::Stacked(s_item) => {
|
||||
Some(s_item)
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
})
|
||||
.find(|s_item| {
|
||||
s_item.tool == *tool
|
||||
})
|
||||
.map(|item| {
|
||||
item.item_id
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option<InventoryItemHandle> {
|
||||
let (slot, _) = self.items.iter()
|
||||
.enumerate()
|
||||
@ -506,10 +601,80 @@ impl CharacterInventory {
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn add_item(&mut self, item: InventoryItem) -> Result<(), InventoryAddError> { // TODO: errors
|
||||
// TODO: check slot conflict?
|
||||
pub fn take_stacked_item_by_id(&mut self, item_id: ClientItemId, amount: usize) -> Option<StackedInventoryItem> {
|
||||
let idx = self.items
|
||||
.iter_mut()
|
||||
.position(|i| i.item_id() == item_id)?;
|
||||
let item: &mut StackedInventoryItem = self.items.get_mut(idx)?.stacked_mut()?;
|
||||
match item.entity_ids.len().cmp(&amount) {
|
||||
Ordering::Equal => {
|
||||
let item = self.items.remove(idx);
|
||||
item.stacked().cloned()
|
||||
},
|
||||
Ordering::Greater => {
|
||||
let entity_ids = item.entity_ids.drain(..amount).collect();
|
||||
Some(StackedInventoryItem {
|
||||
entity_ids,
|
||||
tool: item.tool,
|
||||
item_id: item.item_id,
|
||||
})
|
||||
},
|
||||
Ordering::Less => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_item(&mut self, item: InventoryItem) {
|
||||
self.items.push(item);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_stacked_item(&mut self, mut item: StackedInventoryItem) {
|
||||
let existing_item = self.items
|
||||
.iter_mut()
|
||||
.filter_map(|i| {
|
||||
match i {
|
||||
InventoryItem::Stacked(stacked) => {
|
||||
Some(stacked)
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
})
|
||||
.find(|i| {
|
||||
i.tool == item.tool
|
||||
});
|
||||
|
||||
match existing_item {
|
||||
Some(existing_item) => {
|
||||
existing_item.entity_ids.append(&mut item.entity_ids)
|
||||
},
|
||||
None => {
|
||||
self.items.push(InventoryItem::Stacked(item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_item_with_new_item_id(&mut self, item: InventoryItem, item_id: ClientItemId) {
|
||||
match item {
|
||||
InventoryItem::Individual(mut individual_inventory_item) => {
|
||||
individual_inventory_item.item_id = item_id;
|
||||
self.add_item(InventoryItem::Individual(individual_inventory_item));
|
||||
},
|
||||
InventoryItem::Stacked(mut stacked_inventory_item) => {
|
||||
stacked_inventory_item.item_id = item_id;
|
||||
self.add_stacked_item(stacked_inventory_item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_individual_floor_item(&mut self, floor_item: &IndividualFloorItem) -> &InventoryItem {
|
||||
self.items.push(InventoryItem::Individual(IndividualInventoryItem {
|
||||
entity_id: floor_item.entity_id,
|
||||
item_id: floor_item.item_id,
|
||||
item: floor_item.item.clone(),
|
||||
}));
|
||||
|
||||
self.items.last().unwrap()
|
||||
}
|
||||
|
||||
// TODO: should these pick up functions take floor_item as mut and remove the ids?
|
||||
@ -532,6 +697,33 @@ impl CharacterInventory {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_stacked_floor_item(&mut self, floor_item: &StackedFloorItem) {
|
||||
let existing_item = self.items.iter_mut()
|
||||
.filter_map(|item| {
|
||||
match item {
|
||||
InventoryItem::Stacked(s_item) => Some(s_item),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
})
|
||||
.find(|item| {
|
||||
item.tool == floor_item.tool
|
||||
});
|
||||
|
||||
match existing_item {
|
||||
Some(item) => {
|
||||
item.entity_ids.append(&mut floor_item.entity_ids.clone())
|
||||
},
|
||||
None => {
|
||||
self.items.push(InventoryItem::Stacked(StackedInventoryItem {
|
||||
entity_ids: floor_item.entity_ids.clone(),
|
||||
item_id: floor_item.item_id,
|
||||
tool: floor_item.tool,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: can be simplified using find instead of position
|
||||
pub fn pick_up_stacked_floor_item(&mut self, floor_item: &StackedFloorItem) -> Option<(&StackedInventoryItem, InventorySlot)> {
|
||||
let existing_stack_position = self.items.iter()
|
||||
@ -705,7 +897,7 @@ impl CharacterInventory {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_inventory_entity(&self, character_id: &CharacterEntityId) -> InventoryEntity {
|
||||
pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity {
|
||||
InventoryEntity {
|
||||
items: self.items.iter()
|
||||
.map(|item| {
|
||||
@ -713,9 +905,6 @@ impl CharacterInventory {
|
||||
InventoryItem::Individual(item) => {
|
||||
InventoryItemEntity::Individual(ItemEntity {
|
||||
id: item.entity_id,
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: *character_id,
|
||||
},
|
||||
item: item.item.clone(),
|
||||
})
|
||||
},
|
||||
@ -724,9 +913,6 @@ impl CharacterInventory {
|
||||
.map(|id| {
|
||||
ItemEntity {
|
||||
id: *id,
|
||||
location: ItemLocation::Inventory {
|
||||
character_id: *character_id,
|
||||
},
|
||||
item: ItemDetail::Tool(items.tool)
|
||||
}
|
||||
})
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,10 +2,11 @@ mod bank;
|
||||
mod floor;
|
||||
pub mod inventory;
|
||||
mod manager;
|
||||
pub mod transaction;
|
||||
pub mod use_tool;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)]
|
||||
pub struct ClientItemId(pub u32);
|
||||
|
||||
// TODO: remove these and fix use statements in the rest of the codebase
|
||||
|
337
src/ship/items/transaction.rs
Normal file
337
src/ship/items/transaction.rs
Normal file
@ -0,0 +1,337 @@
|
||||
use crate::entity::gateway::EntityGateway;
|
||||
use thiserror::Error;
|
||||
use crate::ship::items::manager::{ItemManager, ItemManagerError};
|
||||
use crate::entity::gateway::GatewayError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TransactionCommitError {
|
||||
#[error("transaction commit gateway error {0}")]
|
||||
Gateway(#[from] GatewayError),
|
||||
#[error("transaction commit itemmanager error {0}")]
|
||||
ItemManager(#[from] ItemManagerError),
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ItemAction<EG: EntityGateway>: std::marker::Send + std::marker::Sync + std::fmt::Debug {
|
||||
async fn commit(&self, manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError>;
|
||||
}
|
||||
|
||||
pub struct ItemTransactionActions<'a, EG: EntityGateway> {
|
||||
action_queue: Vec<Box<dyn ItemAction<EG>>>,
|
||||
pub manager: &'a ItemManager,
|
||||
}
|
||||
|
||||
|
||||
impl<'a, EG: EntityGateway> ItemTransactionActions<'a, EG> {
|
||||
fn new(manager: &'a ItemManager) -> ItemTransactionActions<'a, EG> {
|
||||
ItemTransactionActions {
|
||||
action_queue: Vec::new(),
|
||||
manager
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action(&mut self, action: Box<dyn ItemAction<EG>>) {
|
||||
self.action_queue.push(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct ItemTransaction<'a, T, EG: EntityGateway> {
|
||||
data: T,
|
||||
actions: ItemTransactionActions<'a, EG>,
|
||||
}
|
||||
|
||||
impl<'a, T, EG: EntityGateway> ItemTransaction<'a, T, EG> {
|
||||
pub fn new(manager: &'a ItemManager, arg: T) -> ItemTransaction<'a, T, EG> {
|
||||
ItemTransaction {
|
||||
data: arg,
|
||||
actions: ItemTransactionActions::new(manager),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn act<E: std::fmt::Debug, U>(mut self, action: fn(&mut ItemTransactionActions<EG>, &T) -> Result<U, E>) -> FinalizedItemTransaction<U, E, EG> {
|
||||
match action(&mut self.actions, &self.data) {
|
||||
Ok(k) => {
|
||||
FinalizedItemTransaction {
|
||||
value: Ok(k),
|
||||
action_queue: self.actions.action_queue,
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
FinalizedItemTransaction {
|
||||
value: Err(err),
|
||||
action_queue: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TransactionError<E: std::fmt::Debug> {
|
||||
#[error("transaction action error {0:?}")]
|
||||
Action(E),
|
||||
#[error("transaction commit error {0}")]
|
||||
Commit(#[from] TransactionCommitError),
|
||||
|
||||
}
|
||||
|
||||
// this only exists to drop the ItemManager borrow of ItemTransaction so a mutable ItemTransaction can be passed in later
|
||||
pub struct FinalizedItemTransaction<T, E: std::fmt::Debug, EG: EntityGateway> {
|
||||
value: Result<T, E>,
|
||||
action_queue: Vec<Box<dyn ItemAction<EG>>>,
|
||||
}
|
||||
|
||||
impl<T, E: std::fmt::Debug, EG: EntityGateway> FinalizedItemTransaction<T, E, EG> {
|
||||
pub async fn commit(self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<T, TransactionError<E>> {
|
||||
match self.value {
|
||||
Ok(value) => {
|
||||
for action in self.action_queue.into_iter() {
|
||||
// TODO: better handle rolling back if this ever errors out
|
||||
action.commit(item_manager, entity_gateway).await.map_err(|err| TransactionError::Commit(err))?;
|
||||
}
|
||||
Ok(value)
|
||||
},
|
||||
Err(err) => Err(TransactionError::Action(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::entity::account::{UserAccountId, NewUserAccountEntity, UserAccountEntity};
|
||||
use crate::entity::character::{NewCharacterEntity, CharacterEntity};
|
||||
use crate::entity::gateway::GatewayError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_item_transaction() {
|
||||
#[derive(Debug)]
|
||||
struct DummyAction1 {
|
||||
name: String,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct DummyAction2 {
|
||||
value: u32,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("")]
|
||||
enum DummyError {
|
||||
Error
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct DummyGateway {
|
||||
d1_set: String,
|
||||
d2_inc: u32,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl EntityGateway for DummyGateway {
|
||||
async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
|
||||
self.d1_set = user.username;
|
||||
Ok(UserAccountEntity::default())
|
||||
}
|
||||
|
||||
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
||||
self.d2_inc += char.slot;
|
||||
Ok(CharacterEntity::default())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 {
|
||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||
item_manager.id_counter = 55555;
|
||||
entity_gateway.create_user(NewUserAccountEntity {
|
||||
username: self.name.clone(),
|
||||
..NewUserAccountEntity::default()
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 {
|
||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||
item_manager.id_counter += self.value;
|
||||
entity_gateway.create_character(NewCharacterEntity {
|
||||
slot: self.value,
|
||||
..NewCharacterEntity::new(UserAccountId(0))
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut item_manager = ItemManager::default();
|
||||
let mut entity_gateway = DummyGateway::default();
|
||||
|
||||
let result = ItemTransaction::new(&item_manager, 12)
|
||||
.act(|it, k| {
|
||||
it.action(Box::new(DummyAction1 {name: "asdf".into()}));
|
||||
it.action(Box::new(DummyAction2 {value: 11}));
|
||||
it.action(Box::new(DummyAction2 {value: *k}));
|
||||
if *k == 99 {
|
||||
return Err(DummyError::Error)
|
||||
}
|
||||
Ok(String::from("hello"))
|
||||
})
|
||||
.commit(&mut item_manager, &mut entity_gateway)
|
||||
.await;
|
||||
|
||||
assert!(entity_gateway.d1_set == "asdf");
|
||||
assert!(entity_gateway.d2_inc == 23);
|
||||
assert!(item_manager.id_counter == 55578);
|
||||
assert!(result.unwrap() == "hello");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_item_transaction_with_action_error() {
|
||||
#[derive(Debug)]
|
||||
struct DummyAction1 {
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct DummyAction2 {
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
||||
#[error("")]
|
||||
enum DummyError {
|
||||
Error
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct DummyGateway {
|
||||
d1_set: String,
|
||||
d2_inc: u32,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl EntityGateway for DummyGateway {
|
||||
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
||||
self.d2_inc += char.slot;
|
||||
Ok(CharacterEntity::default())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 {
|
||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||
entity_gateway.create_character(NewCharacterEntity {
|
||||
slot: 1,
|
||||
..NewCharacterEntity::new(UserAccountId(0))
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 {
|
||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||
entity_gateway.create_character(NewCharacterEntity {
|
||||
slot: 1,
|
||||
..NewCharacterEntity::new(UserAccountId(0))
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut item_manager = ItemManager::default();
|
||||
let mut entity_gateway = DummyGateway::default();
|
||||
|
||||
let result = ItemTransaction::new(&item_manager, 12)
|
||||
.act(|it, _| -> Result<(), _> {
|
||||
it.action(Box::new(DummyAction1 {}));
|
||||
it.action(Box::new(DummyAction2 {}));
|
||||
it.action(Box::new(DummyAction2 {}));
|
||||
Err(DummyError::Error)
|
||||
})
|
||||
.commit(&mut item_manager, &mut entity_gateway)
|
||||
.await;
|
||||
|
||||
assert!(entity_gateway.d2_inc == 0);
|
||||
assert!(matches!(result, Err(TransactionError::Action(DummyError::Error))));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_item_transaction_with_commit_error() {
|
||||
#[derive(Debug)]
|
||||
struct DummyAction1 {
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct DummyAction2 {
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
||||
#[error("")]
|
||||
enum DummyError {
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct DummyGateway {
|
||||
d1_set: String,
|
||||
d2_inc: u32,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl EntityGateway for DummyGateway {
|
||||
async fn create_character(&mut self, char: NewCharacterEntity) -> Result<CharacterEntity, GatewayError> {
|
||||
self.d2_inc += char.slot;
|
||||
Ok(CharacterEntity::default())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction1 {
|
||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||
entity_gateway.create_character(NewCharacterEntity {
|
||||
slot: 1,
|
||||
..NewCharacterEntity::new(UserAccountId(0))
|
||||
})
|
||||
.await?;
|
||||
Err(GatewayError::Error.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<EG: EntityGateway> ItemAction<EG> for DummyAction2 {
|
||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||
entity_gateway.create_character(NewCharacterEntity {
|
||||
slot: 1,
|
||||
..NewCharacterEntity::new(UserAccountId(0))
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut item_manager = ItemManager::default();
|
||||
let mut entity_gateway = DummyGateway::default();
|
||||
|
||||
let result = ItemTransaction::new(&item_manager, 12)
|
||||
.act(|it, _| -> Result<_, DummyError> {
|
||||
it.action(Box::new(DummyAction1 {}));
|
||||
it.action(Box::new(DummyAction2 {}));
|
||||
it.action(Box::new(DummyAction2 {}));
|
||||
Ok(())
|
||||
})
|
||||
.commit(&mut item_manager, &mut entity_gateway)
|
||||
.await;
|
||||
|
||||
// in an ideal world this would be 0 as rollbacks would occur
|
||||
assert!(entity_gateway.d2_inc == 1);
|
||||
assert!(matches!(result, Err(TransactionError::Commit(TransactionCommitError::Gateway(GatewayError::Error)))));
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ pub enum AreaType {
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct LobbyId(pub usize);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)]
|
||||
pub struct RoomId(pub usize);
|
||||
|
||||
impl LobbyId {
|
||||
@ -26,7 +26,7 @@ impl LobbyId {
|
||||
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
#[error("")]
|
||||
#[error("create room")]
|
||||
pub enum CreateRoomError {
|
||||
NoOpenSlots,
|
||||
ClientInAreaAlready,
|
||||
@ -34,7 +34,7 @@ pub enum CreateRoomError {
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
#[error("")]
|
||||
#[error("join room")]
|
||||
pub enum JoinRoomError {
|
||||
RoomDoesNotExist,
|
||||
RoomFull,
|
||||
@ -42,7 +42,7 @@ pub enum JoinRoomError {
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
#[error("")]
|
||||
#[error("join lobby")]
|
||||
pub enum JoinLobbyError {
|
||||
LobbyDoesNotExist,
|
||||
LobbyFull,
|
||||
@ -50,7 +50,7 @@ pub enum JoinLobbyError {
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
#[error("")]
|
||||
#[error("get area")]
|
||||
pub enum GetAreaError {
|
||||
NotInRoom,
|
||||
NotInLobby,
|
||||
@ -58,28 +58,28 @@ pub enum GetAreaError {
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
#[error("")]
|
||||
#[error("client removal")]
|
||||
pub enum ClientRemovalError {
|
||||
ClientNotInArea,
|
||||
InvalidArea,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
#[error("")]
|
||||
#[error("get clients")]
|
||||
pub enum GetClientsError {
|
||||
InvalidClient,
|
||||
InvalidArea,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
#[error("")]
|
||||
#[error("get neighbor")]
|
||||
pub enum GetNeighborError {
|
||||
InvalidClient,
|
||||
InvalidArea,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
#[error("")]
|
||||
#[error("get leader")]
|
||||
pub enum GetLeaderError {
|
||||
InvalidClient,
|
||||
InvalidArea,
|
||||
@ -87,7 +87,7 @@ pub enum GetLeaderError {
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
#[error("")]
|
||||
#[error("clientlocation")]
|
||||
pub enum ClientLocationError {
|
||||
CreateRoomError(#[from] CreateRoomError),
|
||||
JoinRoomError(#[from] JoinRoomError),
|
||||
@ -109,6 +109,12 @@ impl LocalClientId {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<u8> for LocalClientId {
|
||||
fn eq(&self, other: &u8) -> bool {
|
||||
self.0 == *other as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct AreaClient {
|
||||
pub client: ClientId,
|
||||
|
@ -287,18 +287,11 @@ impl MapAreaLookup {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MapAreaLookupBuilder {
|
||||
map_areas: HashMap<u16, MapArea>,
|
||||
}
|
||||
|
||||
impl Default for MapAreaLookupBuilder {
|
||||
fn default() -> MapAreaLookupBuilder {
|
||||
MapAreaLookupBuilder {
|
||||
map_areas: HashMap::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MapAreaLookupBuilder {
|
||||
pub fn add(mut self, value: u16, map_area: MapArea) -> MapAreaLookupBuilder {
|
||||
self.map_areas.insert(value, map_area);
|
||||
|
@ -14,17 +14,17 @@ pub struct RawMapEnemy {
|
||||
id: u32,
|
||||
_unknown1: u16,
|
||||
pub children: u16,
|
||||
map_area: u16,
|
||||
_map_area: u16,
|
||||
_unknown4: u16,
|
||||
section: u16,
|
||||
wave_idd: u16,
|
||||
wave_id: u32,
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
xrot: u32,
|
||||
yrot: u32,
|
||||
zrot: u32,
|
||||
_section: u16,
|
||||
_wave_idd: u16,
|
||||
_wave_id: u32,
|
||||
_x: f32,
|
||||
_y: f32,
|
||||
_z: f32,
|
||||
_xrot: u32,
|
||||
_yrot: u32,
|
||||
_zrot: u32,
|
||||
_field1: u32,
|
||||
field2: u32,
|
||||
_field3: u32,
|
||||
@ -40,17 +40,17 @@ impl RawMapEnemy {
|
||||
id: cursor.read_u32::<LittleEndian>()?,
|
||||
_unknown1: cursor.read_u16::<LittleEndian>()?,
|
||||
children: cursor.read_u16::<LittleEndian>()?,
|
||||
map_area: cursor.read_u16::<LittleEndian>()?,
|
||||
_map_area: cursor.read_u16::<LittleEndian>()?,
|
||||
_unknown4: cursor.read_u16::<LittleEndian>()?,
|
||||
section: cursor.read_u16::<LittleEndian>()?,
|
||||
wave_idd: cursor.read_u16::<LittleEndian>()?,
|
||||
wave_id: cursor.read_u32::<LittleEndian>()?,
|
||||
x: cursor.read_f32::<LittleEndian>()?,
|
||||
y: cursor.read_f32::<LittleEndian>()?,
|
||||
z: cursor.read_f32::<LittleEndian>()?,
|
||||
xrot: cursor.read_u32::<LittleEndian>()?,
|
||||
yrot: cursor.read_u32::<LittleEndian>()?,
|
||||
zrot: cursor.read_u32::<LittleEndian>()?,
|
||||
_section: cursor.read_u16::<LittleEndian>()?,
|
||||
_wave_idd: cursor.read_u16::<LittleEndian>()?,
|
||||
_wave_id: cursor.read_u32::<LittleEndian>()?,
|
||||
_x: cursor.read_f32::<LittleEndian>()?,
|
||||
_y: cursor.read_f32::<LittleEndian>()?,
|
||||
_z: cursor.read_f32::<LittleEndian>()?,
|
||||
_xrot: cursor.read_u32::<LittleEndian>()?,
|
||||
_yrot: cursor.read_u32::<LittleEndian>()?,
|
||||
_zrot: cursor.read_u32::<LittleEndian>()?,
|
||||
_field1: cursor.read_u32::<LittleEndian>()?,
|
||||
field2: cursor.read_u32::<LittleEndian>()?,
|
||||
_field3: cursor.read_u32::<LittleEndian>()?,
|
||||
@ -75,7 +75,7 @@ pub enum MapEnemyError {
|
||||
pub struct MapEnemy {
|
||||
pub monster: MonsterType,
|
||||
pub map_area: MapArea,
|
||||
hp: u32,
|
||||
_hp: u32,
|
||||
// TODO: other stats from battleparam
|
||||
pub player_hit: [bool; 4],
|
||||
pub dropped_item: bool,
|
||||
@ -254,7 +254,7 @@ impl MapEnemy {
|
||||
Ok(MapEnemy {
|
||||
monster,
|
||||
map_area: *map_area,
|
||||
hp: 0,
|
||||
_hp: 0,
|
||||
dropped_item: false,
|
||||
gave_exp: false,
|
||||
player_hit: [false; 4],
|
||||
@ -265,7 +265,7 @@ impl MapEnemy {
|
||||
MapEnemy {
|
||||
monster,
|
||||
map_area,
|
||||
hp: 0,
|
||||
_hp: 0,
|
||||
dropped_item: false,
|
||||
gave_exp: false,
|
||||
player_hit: [false; 4],
|
||||
|
@ -13,50 +13,50 @@ use crate::ship::map::*;
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RawMapObject {
|
||||
otype: u16,
|
||||
unknown1: u16,
|
||||
unknown2: u32,
|
||||
id: u16,
|
||||
group: u16,
|
||||
section: u16,
|
||||
unknown3: u16,
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
xrot: u32,
|
||||
yrot: u32,
|
||||
zrot: u32,
|
||||
_unknown1: u16,
|
||||
_unknown2: u32,
|
||||
_id: u16,
|
||||
_group: u16,
|
||||
_section: u16,
|
||||
_unknown3: u16,
|
||||
_x: f32,
|
||||
_y: f32,
|
||||
_z: f32,
|
||||
_xrot: u32,
|
||||
_yrot: u32,
|
||||
_zrot: u32,
|
||||
field1: f32,
|
||||
field2: f32,
|
||||
field3: f32,
|
||||
field4: u32,
|
||||
field5: u32,
|
||||
field6: u32,
|
||||
field7: u32,
|
||||
_field5: u32,
|
||||
_field6: u32,
|
||||
_field7: u32,
|
||||
}
|
||||
|
||||
impl RawMapObject {
|
||||
pub fn from_byte_stream<R: Read>(cursor: &mut R) -> Result<RawMapObject, std::io::Error> {
|
||||
Ok(RawMapObject {
|
||||
otype: cursor.read_u16::<LittleEndian>()?,
|
||||
unknown1: cursor.read_u16::<LittleEndian>()?,
|
||||
unknown2: cursor.read_u32::<LittleEndian>()?,
|
||||
id: cursor.read_u16::<LittleEndian>()?,
|
||||
group: cursor.read_u16::<LittleEndian>()?,
|
||||
section: cursor.read_u16::<LittleEndian>()?,
|
||||
unknown3: cursor.read_u16::<LittleEndian>()?,
|
||||
x: cursor.read_f32::<LittleEndian>()?,
|
||||
y: cursor.read_f32::<LittleEndian>()?,
|
||||
z: cursor.read_f32::<LittleEndian>()?,
|
||||
xrot: cursor.read_u32::<LittleEndian>()?,
|
||||
yrot: cursor.read_u32::<LittleEndian>()?,
|
||||
zrot: cursor.read_u32::<LittleEndian>()?,
|
||||
_unknown1: cursor.read_u16::<LittleEndian>()?,
|
||||
_unknown2: cursor.read_u32::<LittleEndian>()?,
|
||||
_id: cursor.read_u16::<LittleEndian>()?,
|
||||
_group: cursor.read_u16::<LittleEndian>()?,
|
||||
_section: cursor.read_u16::<LittleEndian>()?,
|
||||
_unknown3: cursor.read_u16::<LittleEndian>()?,
|
||||
_x: cursor.read_f32::<LittleEndian>()?,
|
||||
_y: cursor.read_f32::<LittleEndian>()?,
|
||||
_z: cursor.read_f32::<LittleEndian>()?,
|
||||
_xrot: cursor.read_u32::<LittleEndian>()?,
|
||||
_yrot: cursor.read_u32::<LittleEndian>()?,
|
||||
_zrot: cursor.read_u32::<LittleEndian>()?,
|
||||
field1: cursor.read_f32::<LittleEndian>()?,
|
||||
field2: cursor.read_f32::<LittleEndian>()?,
|
||||
field3: cursor.read_f32::<LittleEndian>()?,
|
||||
field4: cursor.read_u32::<LittleEndian>()?,
|
||||
field5: cursor.read_u32::<LittleEndian>()?,
|
||||
field6: cursor.read_u32::<LittleEndian>()?,
|
||||
field7: cursor.read_u32::<LittleEndian>()?,
|
||||
_field5: cursor.read_u32::<LittleEndian>()?,
|
||||
_field6: cursor.read_u32::<LittleEndian>()?,
|
||||
_field7: cursor.read_u32::<LittleEndian>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -11,3 +11,4 @@ pub mod drops;
|
||||
pub mod packet;
|
||||
pub mod quests;
|
||||
pub mod shops;
|
||||
pub mod trade;
|
||||
|
@ -27,7 +27,8 @@ pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result<ItemDr
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_item(area_client: AreaClient, item_id: ClientItemId, item: &item::ItemDetail) -> Result<CreateItem, ShipError> {
|
||||
// TODO: this doesn't need to be a Result, just unwrap try_intos they are guaranteed to succeed
|
||||
pub fn create_individual_item(area_client: AreaClient, item_id: ClientItemId, item: &item::ItemDetail) -> Result<CreateItem, ShipError> {
|
||||
let bytes = item.as_client_bytes();
|
||||
Ok(CreateItem {
|
||||
client: area_client.local_client.id(),
|
||||
@ -39,6 +40,31 @@ pub fn create_item(area_client: AreaClient, item_id: ClientItemId, item: &item::
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: this doesn't need to be a Result, just unwrap try_intos they are guaranteed to succeed
|
||||
pub fn create_stacked_item(area_client: AreaClient, item_id: ClientItemId, tool: &item::tool::Tool, amount: usize) -> Result<CreateItem, ShipError> {
|
||||
let bytes = tool.as_stacked_bytes(amount);
|
||||
Ok(CreateItem {
|
||||
client: area_client.local_client.id(),
|
||||
target: 0,
|
||||
item_data: bytes[0..12].try_into()?,
|
||||
item_id: item_id.0,
|
||||
item_data2: bytes[12..16].try_into()?,
|
||||
unknown: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem {
|
||||
let bytes: [u8; 12] = [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
CreateItem {
|
||||
client: area_client.local_client.id(),
|
||||
target: 0,
|
||||
item_data: bytes,
|
||||
item_id: 0xFFFFFFFF,
|
||||
item_data2: u32::to_le_bytes(amount as u32),
|
||||
unknown: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result<CreateItem, ShipError> {
|
||||
let bytes = item.as_client_bytes();
|
||||
Ok(CreateItem {
|
||||
@ -142,6 +168,15 @@ pub fn player_no_longer_has_item(area_client: AreaClient, item_id: ClientItemId,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn player_no_longer_has_meseta(area_client: AreaClient, amount: u32) -> PlayerNoLongerHasItem {
|
||||
PlayerNoLongerHasItem {
|
||||
client: area_client.local_client.id(),
|
||||
target: 0,
|
||||
item_id: 0xFFFFFFFF,
|
||||
amount,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shop_list<I: ShopItem>(shop_type: u8, items: &[I]) -> ShopList {
|
||||
let items = items.iter()
|
||||
.enumerate()
|
||||
|
@ -26,10 +26,12 @@ pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -
|
||||
pub fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, item_manager: &ItemManager, level_table: &CharacterLevelTable) -> PlayerInfo {
|
||||
let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||
let inventory = item_manager.get_character_inventory(&client.character).unwrap();
|
||||
let meseta = item_manager.get_character_meseta(&client.character.id).unwrap();
|
||||
let character = CharacterBytesBuilder::default()
|
||||
.character(&client.character)
|
||||
.stats(&stats)
|
||||
.level(level - 1)
|
||||
.meseta(*meseta)
|
||||
.build();
|
||||
PlayerInfo {
|
||||
header: player_header(tag, client, area_client),
|
||||
|
0
src/ship/packet/builder/trade.rs
Normal file
0
src/ship/packet/builder/trade.rs
Normal file
@ -137,8 +137,8 @@ where
|
||||
let (item, floor_type) = item_manager.get_floor_item_by_id(&client.character, ClientItemId(pickup_item.item_id))?;
|
||||
let remove_item = builder::message::remove_item_from_floor(area_client, item)?;
|
||||
let create_item = match item {
|
||||
FloorItem::Individual(individual_floor_item) => Some(builder::message::create_item(area_client, item.item_id(), &individual_floor_item.item)?),
|
||||
FloorItem::Stacked(stacked_floor_item) => Some(builder::message::create_item(area_client, item.item_id(), &item::ItemDetail::Tool(stacked_floor_item.tool))?),
|
||||
FloorItem::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id(), &individual_floor_item.item)?),
|
||||
FloorItem::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id(), &stacked_floor_item.tool, stacked_floor_item.count())?),
|
||||
FloorItem::Meseta(_) => None,
|
||||
//_ => Some(builder::message::create_item(area_client, &item)?),
|
||||
};
|
||||
@ -232,8 +232,8 @@ pub async fn send_bank_list(id: ClientId,
|
||||
{
|
||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
let bank_items = item_manager.get_character_bank(&client.character)?;
|
||||
|
||||
let bank_items_pkt = builder::message::bank_item_list(bank_items, client.character.bank_meseta);
|
||||
let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?;
|
||||
let bank_items_pkt = builder::message::bank_item_list(bank_items, bank_meseta.0);
|
||||
Ok(Box::new(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))].into_iter()))
|
||||
}
|
||||
|
||||
@ -252,11 +252,16 @@ where
|
||||
let other_clients_in_area = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
let bank_action_pkts = match bank_interaction.action {
|
||||
BANK_ACTION_DEPOSIT => {
|
||||
let character_meseta = item_manager.get_character_meseta(&client.character.id)?;
|
||||
let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?;
|
||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||
if client.character.meseta >= bank_interaction.meseta_amount && (bank_interaction.meseta_amount + client.character.bank_meseta) <= BANK_MESETA_CAPACITY {
|
||||
client.character.meseta -= bank_interaction.meseta_amount;
|
||||
client.character.bank_meseta += bank_interaction.meseta_amount;
|
||||
entity_gateway.save_character(&client.character).await?;
|
||||
if character_meseta.0 >= bank_interaction.meseta_amount && (bank_interaction.meseta_amount + bank_meseta.0) <= BANK_MESETA_CAPACITY {
|
||||
let (character_meseta, bank_meseta) = item_manager.get_character_and_bank_meseta_mut(&client.character.id)?;
|
||||
character_meseta.0 -= bank_interaction.meseta_amount;
|
||||
bank_meseta.0 += bank_interaction.meseta_amount;
|
||||
entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?;
|
||||
// TODO: BankName
|
||||
entity_gateway.set_bank_meseta(&client.character.id, item::BankName("".into()), *bank_meseta).await?;
|
||||
}
|
||||
Vec::new()
|
||||
}
|
||||
@ -267,11 +272,16 @@ where
|
||||
}
|
||||
},
|
||||
BANK_ACTION_WITHDRAW => {
|
||||
let character_meseta = item_manager.get_character_meseta(&client.character.id)?;
|
||||
let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?;
|
||||
if bank_interaction.item_id == 0xFFFFFFFF {
|
||||
if client.character.meseta + bank_interaction.meseta_amount <= INVENTORY_MESETA_CAPACITY {
|
||||
client.character.meseta += bank_interaction.meseta_amount;
|
||||
client.character.bank_meseta -= bank_interaction.meseta_amount;
|
||||
entity_gateway.save_character(&client.character).await?;
|
||||
if (bank_meseta.0 >= bank_interaction.meseta_amount) && (character_meseta.0 + bank_interaction.meseta_amount <= INVENTORY_MESETA_CAPACITY) {
|
||||
let (character_meseta, bank_meseta) = item_manager.get_character_and_bank_meseta_mut(&client.character.id)?;
|
||||
character_meseta.0 += bank_interaction.meseta_amount;
|
||||
bank_meseta.0 -= bank_interaction.meseta_amount;
|
||||
entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?;
|
||||
// TODO: BankName
|
||||
entity_gateway.set_bank_meseta(&client.character.id, item::BankName("".into()), *bank_meseta).await?;
|
||||
}
|
||||
Vec::new()
|
||||
}
|
||||
@ -370,12 +380,13 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
if client.character.meseta < item.price() as u32 {
|
||||
let character_meseta = item_manager.get_character_meseta_mut(&client.character.id)?;
|
||||
if character_meseta.0 < item.price() as u32 {
|
||||
return Err(ShipError::ShopError.into())
|
||||
}
|
||||
|
||||
client.character.meseta -= item.price() as u32;
|
||||
entity_gateway.save_character(&client.character).await?;
|
||||
character_meseta.0 -= item.price() as u32;
|
||||
entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?;
|
||||
|
||||
let inventory_item = item_manager.player_buys_item(entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as usize).await?;
|
||||
let create = builder::message::create_withdrawn_inventory_item(area_client, inventory_item)?;
|
||||
@ -447,8 +458,9 @@ where
|
||||
grind: grind_mod,
|
||||
});
|
||||
|
||||
client.character.meseta -= 100;
|
||||
entity_gateway.save_character(&client.character).await?;
|
||||
let character_meseta = item_manager.get_character_meseta_mut(&client.character.id)?;
|
||||
character_meseta.0 -= 100;
|
||||
entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?;
|
||||
|
||||
let preview_pkt = builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?;
|
||||
|
||||
@ -480,7 +492,7 @@ where
|
||||
};
|
||||
let weapon = item_manager.replace_item_with_tekked(entity_gateway, &client.character, item_id, modifier).await?;
|
||||
|
||||
let create_item_pkt = builder::message::create_item(area_client, item_id, &item::ItemDetail::Weapon(weapon))?;
|
||||
let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &item::ItemDetail::Weapon(weapon))?;
|
||||
|
||||
let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(Box::new(neighbors.into_iter()
|
||||
|
@ -22,12 +22,14 @@ pub fn block_selected(id: ClientId,
|
||||
let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||
|
||||
let inventory = item_manager.get_character_inventory(&client.character).unwrap();
|
||||
let meseta = item_manager.get_character_meseta(&client.character.id).unwrap();
|
||||
let bank = item_manager.get_character_bank(&client.character).unwrap();
|
||||
|
||||
let fc = FullCharacterBytesBuilder::default()
|
||||
.character(&client.character)
|
||||
.stats(&stats)
|
||||
.level(level)
|
||||
.meseta(*meseta)
|
||||
.inventory(inventory)
|
||||
.bank(bank)
|
||||
.key_config(&client.settings.settings.key_config)
|
||||
|
@ -135,13 +135,26 @@ where
|
||||
let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, no_longer_has_item.amount as u32).await?;
|
||||
|
||||
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?;
|
||||
let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount as u32);
|
||||
client.item_drop_location = None;
|
||||
|
||||
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
Ok(Box::new(clients_in_area.into_iter()
|
||||
.map(move |c| {
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_meseta_pkt.clone()))))
|
||||
})))
|
||||
std::iter::once((c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_meseta_pkt.clone())))))
|
||||
.chain(
|
||||
if c.client != id {
|
||||
Box::new(std::iter::once(
|
||||
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(no_longer_has_meseta_pkt.clone()))))
|
||||
)) as Box<dyn Iterator<Item = _> + Send>
|
||||
}
|
||||
else {
|
||||
Box::new(std::iter::empty()) as Box<dyn Iterator<Item = _> + Send>
|
||||
}
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
))
|
||||
}
|
||||
else {
|
||||
let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, no_longer_has_item.amount as usize).await?;
|
||||
@ -181,7 +194,7 @@ pub fn update_player_position(id: ClientId,
|
||||
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
|
||||
.as_ref()
|
||||
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?;
|
||||
|
||||
|
||||
match &message.msg {
|
||||
GameMessage::PlayerChangedMap(p) => {
|
||||
client.x = p.x;
|
||||
@ -245,18 +258,22 @@ pub fn update_player_position(id: ClientId,
|
||||
pub async fn charge_attack<EG>(id: ClientId,
|
||||
charge: &ChargeAttack,
|
||||
clients: &mut Clients,
|
||||
entity_gateway: &mut EG)
|
||||
entity_gateway: &mut EG,
|
||||
item_manager: &mut ItemManager)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
where
|
||||
EG: EntityGateway
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
if client.character.meseta >= charge.meseta {
|
||||
client.character.meseta -= charge.meseta;
|
||||
entity_gateway.save_character(&client.character).await?;
|
||||
let meseta = item_manager.get_character_meseta_mut(&client.character.id)?;
|
||||
|
||||
if meseta.0 >= charge.meseta {
|
||||
meseta.0 -= charge.meseta;
|
||||
entity_gateway.set_character_meseta(&client.character.id, *meseta).await?;
|
||||
// TODO: this should probably echo the packet
|
||||
Ok(Box::new(None.into_iter()))
|
||||
} else {
|
||||
Err(ShipError::NotEnoughMeseta(id, client.character.meseta).into())
|
||||
Err(ShipError::NotEnoughMeseta(id, meseta.0).into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,18 +297,21 @@ where
|
||||
pub async fn player_used_medical_center<EG>(id: ClientId,
|
||||
_pumc: &PlayerUsedMedicalCenter, // not needed?
|
||||
entity_gateway: &mut EG,
|
||||
clients: &mut Clients)
|
||||
clients: &mut Clients,
|
||||
item_manager: &mut ItemManager)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
where
|
||||
EG: EntityGateway
|
||||
{
|
||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||
if client.character.meseta >= 10 {
|
||||
client.character.meseta -= 10;
|
||||
entity_gateway.save_character(&client.character).await?;
|
||||
let meseta = item_manager.get_character_meseta_mut(&client.character.id)?;
|
||||
if meseta.0 >= 10 {
|
||||
meseta.0 -= 10;
|
||||
entity_gateway.set_character_meseta(&client.character.id, *meseta).await?;
|
||||
// TODO: this should probably echo the packet
|
||||
Ok(Box::new(None.into_iter()))
|
||||
} else {
|
||||
Err(ShipError::NotEnoughMeseta(id, client.character.meseta).into())
|
||||
Err(ShipError::NotEnoughMeseta(id, meseta.0).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,3 +7,4 @@ pub mod room;
|
||||
pub mod settings;
|
||||
pub mod quest;
|
||||
pub mod ship;
|
||||
pub mod trade;
|
||||
|
544
src/ship/packet/handler/trade.rs
Normal file
544
src/ship/packet/handler/trade.rs
Normal file
@ -0,0 +1,544 @@
|
||||
use std::convert::TryInto;
|
||||
use libpso::packet::ship::*;
|
||||
use libpso::packet::messages::*;
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients};
|
||||
use crate::ship::location::{ClientLocation, ClientLocationError};
|
||||
use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, ItemToTradeDetail};
|
||||
use crate::ship::items::inventory::InventoryItem;
|
||||
use crate::ship::trade::{TradeItem, TradeState, TradeStatus};
|
||||
use crate::entity::gateway::EntityGateway;
|
||||
use crate::ship::packet::builder;
|
||||
|
||||
pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01);
|
||||
pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF);
|
||||
|
||||
|
||||
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
||||
pub enum TradeError {
|
||||
#[error("no partner")]
|
||||
CouldNotFindTradePartner,
|
||||
#[error("invalid item id")]
|
||||
InvalidItemId(ClientItemId),
|
||||
#[error("item does not match id")]
|
||||
ClientItemIdDidNotMatchItem(ClientItemId, [u8; 16]),
|
||||
#[error("invalid stack {1}")]
|
||||
InvalidStackAmount(ClientItemId, usize),
|
||||
#[error("not in trade menu")]
|
||||
NotInTradeMenu,
|
||||
#[error("trade menu at an invalid point")]
|
||||
MismatchedStatus,
|
||||
#[error("no space in inventory")]
|
||||
NoInventorySpace,
|
||||
#[error("no space in stack")]
|
||||
NoStackSpace,
|
||||
#[error("invalid meseta amount")]
|
||||
InvalidMeseta,
|
||||
#[error("tried starting a trade while in one already")]
|
||||
ClientAlreadyInTrade,
|
||||
#[error("tried starting a trade while with player already in a trade")]
|
||||
OtherAlreadyInTrade,
|
||||
#[error("tried to trade item not specified in trade request")]
|
||||
SketchyTrade,
|
||||
#[error("items in trade window and items attempted to trade do not match")]
|
||||
MismatchedTradeItems,
|
||||
}
|
||||
|
||||
|
||||
// TODO: remove target
|
||||
pub async fn trade_request(id: ClientId,
|
||||
trade_request: &TradeRequest,
|
||||
target: u32,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_manager: &mut ItemManager,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
{
|
||||
let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet
|
||||
match trade_request.trade {
|
||||
TradeRequestCommand::Initialize(ref act, _meseta) => {
|
||||
match act {
|
||||
TradeRequestInitializeCommand::Initialize => {
|
||||
if trades.in_trade(&id) {
|
||||
return Err(TradeError::ClientAlreadyInTrade.into())
|
||||
}
|
||||
let trade_partner = client_location.get_client_neighbors(id)?
|
||||
.into_iter()
|
||||
.find(|ac| {
|
||||
ac.local_client.id() == target as u8 //trade_request.client
|
||||
})
|
||||
.ok_or(TradeError::CouldNotFindTradePartner)?;
|
||||
if trades.in_trade(&trade_partner.client) {
|
||||
return Err(TradeError::OtherAlreadyInTrade.into())
|
||||
}
|
||||
trades.new_trade(&id, &trade_partner.client);
|
||||
Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
},
|
||||
TradeRequestInitializeCommand::Respond => {
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
||||
if this.status == TradeStatus::ReceivedRequest && other.status == TradeStatus::SentRequest {
|
||||
this.status = TradeStatus::Trading;
|
||||
other.status = TradeStatus::Trading;
|
||||
|
||||
let trade_request = trade_request.clone();
|
||||
Some(Box::new(client_location.get_all_clients_by_client(id).ok()?.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|| -> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||
trades.remove_trade(&id);
|
||||
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
}
|
||||
}
|
||||
},
|
||||
TradeRequestCommand::AddItem(item_id, amount) => {
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
||||
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
||||
let inventory = item_manager.get_character_inventory(&client.character)?;
|
||||
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
||||
this.meseta += amount as usize;
|
||||
}
|
||||
else {
|
||||
let item = inventory.get_item_by_id(ClientItemId(item_id)).ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item_id)))?;
|
||||
|
||||
match item {
|
||||
InventoryItem::Individual(_) => {
|
||||
this.items.push(TradeItem::Individual(ClientItemId(item_id)));
|
||||
},
|
||||
InventoryItem::Stacked(stacked_item) => {
|
||||
if stacked_item.count() < amount as usize {
|
||||
return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into());
|
||||
}
|
||||
this.items.push(TradeItem::Stacked(ClientItemId(item_id), amount as usize));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let trade_request = trade_request.clone();
|
||||
Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
}
|
||||
else {
|
||||
Err(TradeError::MismatchedStatus.into())
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|_err| {
|
||||
trades.remove_trade(&id);
|
||||
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
},
|
||||
TradeRequestCommand::RemoveItem(item_id, amount) => {
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
||||
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
||||
let client = clients.get(&this.client())?; //.ok_or(ShipError::ClientNotFound(id)).ok()?;
|
||||
let inventory = item_manager.get_character_inventory(&client.character).ok()?;
|
||||
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
||||
this.meseta -= amount as usize;
|
||||
}
|
||||
else {
|
||||
let item = inventory.get_item_by_id(ClientItemId(item_id))?;
|
||||
|
||||
match item {
|
||||
InventoryItem::Individual(_) => {
|
||||
this.items.retain(|item| {
|
||||
item.item_id() != ClientItemId(item_id)
|
||||
})
|
||||
},
|
||||
InventoryItem::Stacked(_stacked_item) => {
|
||||
let trade_item_index = this.items.iter()
|
||||
.position(|item| {
|
||||
item.item_id() == ClientItemId(item_id)
|
||||
})?;
|
||||
|
||||
match this.items[trade_item_index].stacked()?.1.cmp(&(amount as usize)) {
|
||||
std::cmp::Ordering::Greater => {
|
||||
*this.items[trade_item_index].stacked_mut()?.1 -= amount as usize;
|
||||
},
|
||||
std::cmp::Ordering::Equal => {
|
||||
this.items.remove(trade_item_index);
|
||||
},
|
||||
std::cmp::Ordering::Less => {
|
||||
return None
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
let trade_request = trade_request.clone();
|
||||
Some(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|| {
|
||||
trades.remove_trade(&id);
|
||||
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
},
|
||||
TradeRequestCommand::Confirm => {
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
||||
if status_is(&this.status, &[TradeStatus::Trading]) && status_is(&other.status, &[TradeStatus::Trading, TradeStatus::Confirmed]) {
|
||||
this.status = TradeStatus::Confirmed;
|
||||
|
||||
let trade_request = trade_request.clone();
|
||||
Some(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|| {
|
||||
trades.remove_trade(&id);
|
||||
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
},
|
||||
TradeRequestCommand::FinalConfirm => {
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
||||
if this.status == TradeStatus::Confirmed && (other.status == TradeStatus::Confirmed || other.status == TradeStatus::FinalConfirm) {
|
||||
this.status = TradeStatus::FinalConfirm;
|
||||
|
||||
let trade_request = trade_request.clone();
|
||||
Some(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||
})))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|| {
|
||||
trades.remove_trade(&id);
|
||||
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
},
|
||||
TradeRequestCommand::Cancel => {
|
||||
trades.remove_trade(&id);
|
||||
Ok(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| client.local_client.id() == target as u8)
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn status_is<const N: usize>(status: &TradeStatus, statuses: &[TradeStatus; N]) -> bool {
|
||||
statuses.iter().any(|s| s == status)
|
||||
}
|
||||
|
||||
fn status_is_not<const N: usize>(status: &TradeStatus, statuses: &[TradeStatus; N]) -> bool {
|
||||
!status_is(status, statuses)
|
||||
}
|
||||
|
||||
pub async fn inner_items_to_trade(id: ClientId,
|
||||
items_to_trade: &ItemsToTrade,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_manager: &mut ItemManager,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
{
|
||||
Ok(trades
|
||||
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||
if status_is_not(&this.status, &[TradeStatus::FinalConfirm]) || status_is_not(&other.status, &[TradeStatus::FinalConfirm, TradeStatus::ItemsChecked]) {
|
||||
return Err(TradeError::MismatchedStatus.into())
|
||||
}
|
||||
|
||||
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
||||
let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?;
|
||||
let inventory = item_manager.get_character_inventory(&client.character)?;
|
||||
|
||||
if items_to_trade.count as usize != (this.items.len() + (if this.meseta != 0 { 1 } else { 0 })) {
|
||||
return Err(TradeError::MismatchedTradeItems.into())
|
||||
}
|
||||
|
||||
items_to_trade.items
|
||||
.iter()
|
||||
.take(items_to_trade.count as usize)
|
||||
.map(|item| {
|
||||
if ClientItemId(item.item_id) == OTHER_MESETA_ITEM_ID {
|
||||
if item.item_data[0] != 4 {
|
||||
return Err(TradeError::InvalidItemId(ClientItemId(item.item_id)).into())
|
||||
}
|
||||
let amount = u32::from_le_bytes(item.item_data2);
|
||||
let character_meseta = item_manager.get_character_meseta(&client.character.id).map_err(|_| TradeError::InvalidMeseta)?;
|
||||
let other_character_meseta = item_manager.get_character_meseta(&other_client.character.id).map_err(|_| TradeError::InvalidMeseta)?;
|
||||
if amount > character_meseta.0 {
|
||||
return Err(TradeError::InvalidMeseta.into())
|
||||
}
|
||||
if (amount + other_character_meseta.0) > 999999 {
|
||||
return Err(TradeError::InvalidMeseta.into())
|
||||
}
|
||||
if amount != this.meseta as u32{
|
||||
return Err(TradeError::InvalidMeseta.into())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
let real_item = inventory.get_item_by_id(ClientItemId(item.item_id))
|
||||
.ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?;
|
||||
let real_trade_item = this.items
|
||||
.iter()
|
||||
.find(|i| i.item_id() == ClientItemId(item.item_id))
|
||||
.ok_or(TradeError::SketchyTrade)?;
|
||||
let trade_item_bytes: [u8; 16] = item.item_data.iter()
|
||||
.chain(item.item_data2.iter())
|
||||
.cloned().collect::<Vec<u8>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
match real_item {
|
||||
InventoryItem::Individual(_individual_inventory_item) => {
|
||||
if real_item.as_client_bytes() == trade_item_bytes {
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into())
|
||||
}
|
||||
},
|
||||
InventoryItem::Stacked(stacked_inventory_item) => {
|
||||
if real_item.as_client_bytes()[0..4] == trade_item_bytes[0..4] {
|
||||
let amount = trade_item_bytes[5] as usize;
|
||||
if amount <= stacked_inventory_item.entity_ids.len() {
|
||||
if real_trade_item.stacked().ok_or(TradeError::SketchyTrade)?.1 == amount {
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into())
|
||||
}
|
||||
}
|
||||
else {
|
||||
Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into())
|
||||
}
|
||||
}
|
||||
else {
|
||||
Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
||||
|
||||
this.status = TradeStatus::ItemsChecked;
|
||||
if this.status == TradeStatus::ItemsChecked && other.status == TradeStatus::ItemsChecked {
|
||||
Ok(Box::new(vec![
|
||||
(this.client(), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})),
|
||||
(other.client(), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})),
|
||||
].into_iter()))
|
||||
}
|
||||
else {
|
||||
Ok(Box::new(None.into_iter()))
|
||||
}
|
||||
})?
|
||||
.unwrap_or_else(|err| {
|
||||
log::warn!("trade error: {:?}", err);
|
||||
let (_this, other) = trades.remove_trade(&id);
|
||||
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| other.as_ref().map(|other| client.client == other.client() ).unwrap_or_else(|| false))
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn items_to_trade(id: ClientId,
|
||||
items_to_trade_pkt: &ItemsToTrade,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_manager: &mut ItemManager,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
{
|
||||
let t = inner_items_to_trade(id, items_to_trade_pkt, client_location, clients, item_manager, trades).await;
|
||||
match t {
|
||||
Ok(p) => Ok(p),
|
||||
Err(err) => {
|
||||
log::warn!("atrade error: {:?}", err);
|
||||
let (_this, other) = trades.remove_trade(&id);
|
||||
Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter()
|
||||
.filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false))
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn trade_confirmed<EG>(id: ClientId,
|
||||
entity_gateway: &mut EG,
|
||||
client_location: &ClientLocation,
|
||||
clients: &mut Clients,
|
||||
item_manager: &mut ItemManager,
|
||||
trades: &mut TradeState)
|
||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||
where
|
||||
EG: EntityGateway
|
||||
{
|
||||
enum TradeReady<'a> {
|
||||
OnePlayer,
|
||||
BothPlayers(crate::ship::location::RoomId,
|
||||
(crate::ship::location::AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState),
|
||||
(crate::ship::location::AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)),
|
||||
}
|
||||
|
||||
let trade_instructions = trades
|
||||
.with(&id, |this, other| -> Result<_, anyhow::Error> {
|
||||
if status_is_not(&this.status, &[TradeStatus::ItemsChecked]) || status_is_not(&other.status, &[TradeStatus::ItemsChecked, TradeStatus::TradeComplete]) {
|
||||
return Err(TradeError::MismatchedStatus.into())
|
||||
}
|
||||
this.status = TradeStatus::TradeComplete;
|
||||
|
||||
if this.status == TradeStatus::TradeComplete && other.status == TradeStatus::TradeComplete {
|
||||
let this_client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
||||
let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?;
|
||||
let this_local_client = client_location.get_local_client(this.client())?;
|
||||
let other_local_client = client_location.get_local_client(other.client())?;
|
||||
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||
|
||||
Ok(TradeReady::BothPlayers(room_id,
|
||||
(this_local_client, this_client, this.clone()),
|
||||
(other_local_client, other_client, other.clone())))
|
||||
}
|
||||
else {
|
||||
Ok(TradeReady::OnePlayer)
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: this match needs to handle errors better
|
||||
match trade_instructions {
|
||||
Ok(Ok(trade)) => {
|
||||
match trade {
|
||||
TradeReady::OnePlayer => {
|
||||
Ok(Box::new(None.into_iter()) as Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>)
|
||||
},
|
||||
TradeReady::BothPlayers(room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => {
|
||||
let traded_items = item_manager.trade_items(entity_gateway,
|
||||
room_id,
|
||||
(&this_local_client, &this_client.character, &this.items, this.meseta),
|
||||
(&other_local_client, &other_client.character, &other.items, other.meseta)).await?;
|
||||
|
||||
let clients_in_room = client_location.get_all_clients_by_client(id)?;
|
||||
let traded_item_packets = traded_items
|
||||
.into_iter()
|
||||
.map(|item| {
|
||||
match item.item_detail {
|
||||
ItemToTradeDetail::Individual(item_detail) => {
|
||||
[
|
||||
GameMessage::CreateItem(builder::message::create_individual_item(item.add_to, item.new_item_id, &item_detail).unwrap()),
|
||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, 1)) // TODO: amount = ?
|
||||
]
|
||||
},
|
||||
ItemToTradeDetail::Stacked(tool, amount) => {
|
||||
[
|
||||
GameMessage::CreateItem(builder::message::create_stacked_item(item.add_to, item.new_item_id, &tool, amount).unwrap()),
|
||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, amount as u32))
|
||||
]
|
||||
},
|
||||
ItemToTradeDetail::Meseta(amount) => {
|
||||
[
|
||||
GameMessage::CreateItem(builder::message::create_meseta(item.add_to, amount)),
|
||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, amount as u32))
|
||||
]
|
||||
},
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.map(move |packet| {
|
||||
clients_in_room
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter_map(move |client| {
|
||||
match packet {
|
||||
GameMessage::PlayerNoLongerHasItem(ref no_longer) => {
|
||||
if client.local_client == no_longer.client {
|
||||
None
|
||||
}
|
||||
else {
|
||||
Some((client.client, SendShipPacket::Message(Message::new(packet.clone()))))
|
||||
}
|
||||
}
|
||||
_ => Some((client.client, SendShipPacket::Message(Message::new(packet.clone()))))
|
||||
}
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
let close_trade = vec![
|
||||
(this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())),
|
||||
(other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default()))
|
||||
].into_iter();
|
||||
Ok(Box::new(traded_item_packets.chain(close_trade)))
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
let (_this, other) = trades.remove_trade(&id);
|
||||
Ok(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
.filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false))
|
||||
.map(move |client| {
|
||||
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||
})
|
||||
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))))
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ use crate::ship::quests;
|
||||
use crate::ship::map::{MapsError, MapAreaError, MapArea};
|
||||
use crate::ship::packet::handler;
|
||||
use crate::ship::shops::{WeaponShop, ToolShop, ArmorShop, WeaponShopItem, ToolShopItem, ArmorShopItem};
|
||||
use crate::ship::trade::TradeState;
|
||||
|
||||
pub const SHIP_PORT: u16 = 23423;
|
||||
pub const QUEST_CATEGORY_MENU_ID: u32 = 0xA2;
|
||||
@ -41,11 +42,12 @@ pub type Rooms = [Option<room::RoomState>; MAX_ROOMS];
|
||||
pub type Clients = HashMap<ClientId, ClientState>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("")]
|
||||
#[error("shiperror {0:?}")]
|
||||
pub enum ShipError {
|
||||
ClientNotFound(ClientId),
|
||||
NoCharacterInSlot(ClientId, u32),
|
||||
InvalidSlot(ClientId, u32),
|
||||
#[error("")]
|
||||
TooManyClients,
|
||||
ClientLocationError(#[from] ClientLocationError),
|
||||
GetNeighborError(#[from] GetNeighborError),
|
||||
@ -56,10 +58,12 @@ pub enum ShipError {
|
||||
InvalidRoom(u32),
|
||||
MonsterAlreadyDroppedItem(ClientId, u16),
|
||||
SliceError(#[from] std::array::TryFromSliceError),
|
||||
#[error("")]
|
||||
ItemError, // TODO: refine this
|
||||
PickUpInvalidItemId(u32),
|
||||
DropInvalidItemId(u32),
|
||||
ItemManagerError(#[from] items::ItemManagerError),
|
||||
#[error("")]
|
||||
ItemDropLocationNotSet,
|
||||
BoxAlreadyDroppedItem(ClientId, u16),
|
||||
InvalidQuestCategory(u32),
|
||||
@ -67,12 +71,15 @@ pub enum ShipError {
|
||||
InvalidQuestFilename(String),
|
||||
IoError(#[from] std::io::Error),
|
||||
NotEnoughMeseta(ClientId, u32),
|
||||
#[error("")]
|
||||
ShopError,
|
||||
GatewayError(#[from] GatewayError),
|
||||
UnknownMonster(crate::ship::monster::MonsterType),
|
||||
InvalidShip(usize),
|
||||
InvalidBlock(usize),
|
||||
InvalidItem(items::ClientItemId),
|
||||
#[error("tradeerror {0}")]
|
||||
TradeError(#[from] crate::ship::packet::handler::trade::TradeError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -106,6 +113,8 @@ pub enum RecvShipPacket {
|
||||
SaveOptions(SaveOptions),
|
||||
RequestShipList(RequestShipList),
|
||||
RequestShipBlockList(RequestShipBlockList),
|
||||
ItemsToTrade(ItemsToTrade),
|
||||
TradeConfirmed(TradeConfirmed),
|
||||
}
|
||||
|
||||
impl RecvServerPacket for RecvShipPacket {
|
||||
@ -143,6 +152,8 @@ impl RecvServerPacket for RecvShipPacket {
|
||||
0xA1 => Ok(RecvShipPacket::RequestShipBlockList(RequestShipBlockList::from_bytes(data)?)),
|
||||
0xA2 => Ok(RecvShipPacket::RequestQuestList(RequestQuestList::from_bytes(data)?)),
|
||||
0xAC => Ok(RecvShipPacket::DoneLoadingQuest(DoneLoadingQuest::from_bytes(data)?)),
|
||||
0xD0 => Ok(RecvShipPacket::ItemsToTrade(ItemsToTrade::from_bytes(data)?)),
|
||||
0xD2 => Ok(RecvShipPacket::TradeConfirmed(TradeConfirmed::from_bytes(data)?)),
|
||||
0xE7 => Ok(RecvShipPacket::FullCharacterData(Box::new(FullCharacterData::from_bytes(data)?))),
|
||||
0x1ED => Ok(RecvShipPacket::SaveOptions(SaveOptions::from_bytes(data)?)),
|
||||
_ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec()))
|
||||
@ -184,6 +195,9 @@ pub enum SendShipPacket {
|
||||
DoneLoadingQuest(DoneLoadingQuest),
|
||||
BankItemList(BankItemList),
|
||||
RedirectClient(RedirectClient),
|
||||
AcknowledgeTrade(AcknowledgeTrade),
|
||||
CancelTrade(CancelTrade),
|
||||
TradeSuccessful(TradeSuccessful),
|
||||
}
|
||||
|
||||
impl SendServerPacket for SendShipPacket {
|
||||
@ -221,6 +235,9 @@ impl SendServerPacket for SendShipPacket {
|
||||
SendShipPacket::DoneLoadingQuest(pkt) => pkt.as_bytes(),
|
||||
SendShipPacket::BankItemList(pkt) => pkt.as_bytes(),
|
||||
SendShipPacket::RedirectClient(pkt) => pkt.as_bytes(),
|
||||
SendShipPacket::AcknowledgeTrade(pkt) => pkt.as_bytes(),
|
||||
SendShipPacket::CancelTrade(pkt) => pkt.as_bytes(),
|
||||
SendShipPacket::TradeSuccessful(pkt) => pkt.as_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -239,6 +256,12 @@ pub struct LoadingQuest {
|
||||
//pub quest_chunk_bin: Option<Box<dyn Iterator<Item = >>>,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pub struct ClientState {
|
||||
pub user: UserAccountEntity,
|
||||
pub settings: UserSettingsEntity,
|
||||
@ -380,6 +403,7 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
|
||||
auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())),
|
||||
ship_list: Vec::new(),
|
||||
shipgate_sender: None,
|
||||
trades: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -426,6 +450,7 @@ pub struct ShipServerState<EG: EntityGateway> {
|
||||
auth_token: AuthToken,
|
||||
ship_list: Vec<Ship>,
|
||||
shipgate_sender: Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
|
||||
trades: TradeState,
|
||||
}
|
||||
|
||||
impl<EG: EntityGateway> ShipServerState<EG> {
|
||||
@ -463,14 +488,14 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
||||
handler::message::update_player_position(id, msg, &mut self.clients, &block.client_location, &block.rooms)?
|
||||
},
|
||||
GameMessage::ChargeAttack(charge_attack) => {
|
||||
handler::message::charge_attack(id, charge_attack, &mut self.clients, &mut self.entity_gateway).await?
|
||||
handler::message::charge_attack(id, charge_attack, &mut self.clients, &mut self.entity_gateway, &mut self.item_manager).await?
|
||||
},
|
||||
GameMessage::PlayerUseItem(player_use_item) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
||||
},
|
||||
GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => {
|
||||
handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &mut self.clients).await?
|
||||
handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
|
||||
},
|
||||
GameMessage::PlayerFeedMag(player_feed_mag) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
@ -530,6 +555,9 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
||||
GameMessage::TekAccept(tek_accept) => {
|
||||
handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
||||
},
|
||||
GameMessage::TradeRequest(trade_request) => {
|
||||
handler::trade::trade_request(id, trade_request, target, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await?
|
||||
},
|
||||
_ => {
|
||||
let cmsg = msg.clone();
|
||||
Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||
@ -696,7 +724,15 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
||||
},
|
||||
RecvShipPacket::RequestShipBlockList(_) => {
|
||||
handler::ship::block_list(id, &self.name, self.blocks.0.len())
|
||||
}
|
||||
},
|
||||
RecvShipPacket::ItemsToTrade(items_to_trade) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::trade::items_to_trade(id, items_to_trade, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await?
|
||||
},
|
||||
RecvShipPacket::TradeConfirmed(_) => {
|
||||
let block = self.blocks.with_client(id, &self.clients)?;
|
||||
handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await?
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -315,7 +315,7 @@ fn number_of_weapons_to_generate(character_level: usize) -> usize {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WeaponShop<R: Rng + SeedableRng> {
|
||||
difficulty: Difficulty,
|
||||
_difficulty: Difficulty,
|
||||
section_id: SectionID,
|
||||
weapon: WeaponTable,
|
||||
special: SpecialTable,
|
||||
@ -329,7 +329,7 @@ pub struct WeaponShop<R: Rng + SeedableRng> {
|
||||
impl<R: Rng + SeedableRng> WeaponShop<R> {
|
||||
pub fn new(difficulty: Difficulty, section_id: SectionID) -> WeaponShop<R> {
|
||||
WeaponShop {
|
||||
difficulty,
|
||||
_difficulty: difficulty,
|
||||
section_id,
|
||||
weapon: load_weapon_table(difficulty, section_id),
|
||||
special: load_special_table(),
|
||||
@ -358,8 +358,10 @@ impl<R: Rng + SeedableRng> WeaponShop<R> {
|
||||
.last()
|
||||
.unwrap();
|
||||
|
||||
let special_tier = WeightedIndex::new(tier.special.iter().map(|t| t.probability)).unwrap();
|
||||
match special_tier.sample(&mut self.rng) {
|
||||
let special_tier_choice = WeightedIndex::new(tier.special.iter().map(|t| t.probability)).unwrap();
|
||||
let special_tier_index = special_tier_choice.sample(&mut self.rng);
|
||||
let special_tier = tier.special.get(special_tier_index)?;
|
||||
match special_tier.tier {
|
||||
1 => TIER1_SPECIAL.choose(&mut self.rng).cloned(),
|
||||
2 => TIER2_SPECIAL.choose(&mut self.rng).cloned(),
|
||||
_ => None
|
||||
|
133
src/ship/trade.rs
Normal file
133
src/ship/trade.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use std::collections::HashMap;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::common::serverstate::ClientId;
|
||||
use crate::ship::items;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TradeItem {
|
||||
Individual(items::ClientItemId),
|
||||
Stacked(items::ClientItemId, usize),
|
||||
}
|
||||
|
||||
impl TradeItem {
|
||||
pub fn stacked(&self) -> Option<(items::ClientItemId, usize)> {
|
||||
match self {
|
||||
TradeItem::Stacked(item_id, amount) => Some((*item_id, *amount)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stacked_mut(&mut self) -> Option<(items::ClientItemId, &mut usize)> {
|
||||
match self {
|
||||
TradeItem::Stacked(item_id, ref mut amount) => Some((*item_id, amount)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn item_id(&self) -> items::ClientItemId {
|
||||
match self {
|
||||
TradeItem::Individual(item_id) => *item_id,
|
||||
TradeItem::Stacked(item_id, _) => *item_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum TradeStatus {
|
||||
SentRequest,
|
||||
ReceivedRequest,
|
||||
Trading,
|
||||
Confirmed,
|
||||
FinalConfirm,
|
||||
ItemsChecked,
|
||||
TradeComplete,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientTradeState {
|
||||
client: ClientId,
|
||||
other_client: ClientId,
|
||||
pub items: Vec<TradeItem>,
|
||||
pub meseta: usize,
|
||||
pub status: TradeStatus,
|
||||
}
|
||||
|
||||
|
||||
impl ClientTradeState {
|
||||
pub fn client(&self) -> ClientId {
|
||||
self.client
|
||||
}
|
||||
|
||||
pub fn other_client(&self) -> ClientId {
|
||||
self.other_client
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error("")]
|
||||
pub enum TradeStateError {
|
||||
ClientNotInTrade(ClientId),
|
||||
MismatchedTrade(ClientId, ClientId),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TradeState {
|
||||
trades: HashMap<ClientId, RefCell<ClientTradeState>>,
|
||||
}
|
||||
|
||||
impl TradeState {
|
||||
pub fn new_trade(&mut self, sender: &ClientId, receiver: &ClientId) {
|
||||
let state = ClientTradeState {
|
||||
client: *sender,
|
||||
other_client: *receiver,
|
||||
items: Default::default(),
|
||||
meseta: 0,
|
||||
status: TradeStatus::SentRequest,
|
||||
};
|
||||
self.trades.insert(*sender, RefCell::new(state));
|
||||
|
||||
let state = ClientTradeState {
|
||||
client: *receiver,
|
||||
other_client: *sender,
|
||||
items: Default::default(),
|
||||
meseta: 0,
|
||||
status: TradeStatus::ReceivedRequest,
|
||||
};
|
||||
self.trades.insert(*receiver, RefCell::new(state));
|
||||
}
|
||||
|
||||
pub fn in_trade(&self, client: &ClientId) -> bool {
|
||||
self.trades.contains_key(client)
|
||||
}
|
||||
|
||||
pub fn with<T, F> (&self, client: &ClientId, func: F) -> Result<T, TradeStateError>
|
||||
where
|
||||
F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> T
|
||||
{
|
||||
let mut c1 = self.trades.get(client).ok_or_else(|| TradeStateError::ClientNotInTrade(*client))?.borrow_mut();
|
||||
let mut c2 = self.trades.get(&c1.other_client).ok_or(TradeStateError::ClientNotInTrade(c1.other_client))?.borrow_mut();
|
||||
|
||||
// sanity check
|
||||
if c1.client != c2.other_client {
|
||||
return Err(TradeStateError::MismatchedTrade(c1.client, c2.client));
|
||||
}
|
||||
|
||||
Ok(func(&mut *c1, &mut *c2))
|
||||
}
|
||||
|
||||
// TODO: is it possible for this to not return Options?
|
||||
pub fn remove_trade(&mut self, client: &ClientId) -> (Option<ClientTradeState>, Option<ClientTradeState>) {
|
||||
let c1 = self.trades.remove(client).map(|c| c.into_inner());
|
||||
let c2 = if let Some(ref state) = c1 {
|
||||
self.trades.remove(&state.other_client).map(|c| c.into_inner())
|
||||
}
|
||||
else {
|
||||
None
|
||||
};
|
||||
|
||||
(c1, c2)
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ use elseware::common::serverstate::{ClientId, ServerState};
|
||||
use elseware::entity::gateway::EntityGateway;
|
||||
use elseware::entity::account::{UserAccountEntity, NewUserAccountEntity, NewUserSettingsEntity};
|
||||
use elseware::entity::character::{CharacterEntity, NewCharacterEntity};
|
||||
use elseware::entity::item::{Meseta, BankName};
|
||||
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
|
||||
use elseware::ship::room::Difficulty;
|
||||
|
||||
@ -27,6 +28,8 @@ pub async fn new_user_character<EG: EntityGateway>(entity_gateway: &mut EG, user
|
||||
let _settings = entity_gateway.create_user_settings(new_settings).await.unwrap();
|
||||
let new_character = NewCharacterEntity::new(user.id);
|
||||
let character = entity_gateway.create_character(new_character).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();
|
||||
|
||||
(user, character)
|
||||
}
|
||||
|
@ -29,10 +29,6 @@ async fn test_bank_items_sent_in_character_login() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item]), item::BankName("".into())).await.unwrap();
|
||||
@ -70,10 +66,6 @@ async fn test_request_bank_items() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -118,10 +110,6 @@ async fn test_request_stacked_bank_items() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -167,10 +155,6 @@ async fn test_request_bank_items_sorted() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap();
|
||||
let monomate = entity_gateway.create_item(
|
||||
item::NewItemEntity {
|
||||
@ -179,10 +163,6 @@ async fn test_request_bank_items_sorted() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap();
|
||||
let item2 = entity_gateway.create_item(
|
||||
item::NewItemEntity {
|
||||
@ -195,10 +175,6 @@ async fn test_request_bank_items_sorted() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
let bank = vec![item::BankItemEntity::Individual(item1), vec![monomate].into(), item2.into()];
|
||||
@ -245,9 +221,6 @@ async fn test_deposit_individual_item() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
let item1 = entity_gateway.create_item(
|
||||
item::NewItemEntity {
|
||||
@ -260,9 +233,6 @@ async fn test_deposit_individual_item() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![item0, item1])).await.unwrap();
|
||||
@ -322,9 +292,6 @@ async fn test_deposit_stacked_item() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -386,9 +353,6 @@ async fn test_deposit_partial_stacked_item() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -460,9 +424,6 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
bank_monomates.push(entity_gateway.create_item(
|
||||
@ -472,10 +433,6 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".into()),
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -537,9 +494,6 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -552,10 +506,6 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".into()),
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -619,9 +569,6 @@ async fn test_deposit_individual_item_in_full_bank() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
let mut bank = Vec::new();
|
||||
@ -637,10 +584,6 @@ async fn test_deposit_individual_item_in_full_bank() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -697,9 +640,6 @@ async fn test_deposit_stacked_item_in_full_bank() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -716,10 +656,6 @@ async fn test_deposit_stacked_item_in_full_bank() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -777,9 +713,6 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -792,10 +725,6 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -812,10 +741,6 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap().into());
|
||||
}
|
||||
almost_full_bank.push(bank_monomates.into());
|
||||
@ -860,9 +785,8 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
|
||||
async fn test_deposit_meseta() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.meseta = 300;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -887,20 +811,19 @@ async fn test_deposit_meseta() {
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let char = characters[0].as_ref().unwrap();
|
||||
assert!(char.meseta == 277);
|
||||
assert!(char.bank_meseta == 23);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
||||
assert!(c1_meseta.0 == 277);
|
||||
assert!(c1_bank_meseta.0 == 23);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_deposit_too_much_meseta() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.meseta = 300;
|
||||
char1.bank_meseta = 999980;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap();
|
||||
entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(999980)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -925,21 +848,19 @@ async fn test_deposit_too_much_meseta() {
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let char = characters[0].as_ref().unwrap();
|
||||
assert!(char.meseta == 300);
|
||||
assert!(char.bank_meseta == 999980);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
||||
assert!(c1_meseta.0 == 300);
|
||||
assert!(c1_bank_meseta.0 == 999980);
|
||||
}
|
||||
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_deposit_meseta_when_bank_is_maxed() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.meseta = 300;
|
||||
char1.bank_meseta = 999999;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap();
|
||||
entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(999999)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -964,10 +885,10 @@ async fn test_deposit_meseta_when_bank_is_maxed() {
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let char = characters[0].as_ref().unwrap();
|
||||
assert!(char.meseta == 300);
|
||||
assert!(char.bank_meseta == 999999);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
||||
assert!(c1_meseta.0 == 300);
|
||||
assert!(c1_bank_meseta.0 == 999999);
|
||||
}
|
||||
|
||||
|
||||
@ -990,10 +911,6 @@ async fn test_withdraw_individual_item() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap();
|
||||
@ -1053,10 +970,6 @@ async fn test_withdraw_stacked_item() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -1117,10 +1030,6 @@ async fn test_withdraw_partial_stacked_item() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".into())
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), item::BankName("".into())).await.unwrap();
|
||||
@ -1188,9 +1097,6 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
bank_monomates.push(entity_gateway.create_item(
|
||||
@ -1200,10 +1106,6 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".into()),
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -1267,10 +1169,6 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".into()),
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -1283,9 +1181,6 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -1349,10 +1244,6 @@ async fn test_withdraw_individual_item_in_full_inventory() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
let mut inventory = Vec::new();
|
||||
@ -1368,9 +1259,6 @@ async fn test_withdraw_individual_item_in_full_inventory() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -1423,10 +1311,6 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -1443,9 +1327,6 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -1504,10 +1385,6 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Bank {
|
||||
character_id: char1.id,
|
||||
name: item::BankName("".to_string())
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_item]), item::BankName("".into())).await.unwrap();
|
||||
@ -1525,9 +1402,6 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap().into());
|
||||
}
|
||||
|
||||
@ -1540,9 +1414,6 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
items.push(item::InventoryItemEntity::Stacked(item29));
|
||||
@ -1589,9 +1460,8 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
|
||||
async fn test_withdraw_meseta() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.bank_meseta = 300;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(300)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -1616,20 +1486,19 @@ async fn test_withdraw_meseta() {
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let char = characters[0].as_ref().unwrap();
|
||||
assert!(char.meseta == 23);
|
||||
assert!(char.bank_meseta == 277);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
||||
assert!(c1_meseta.0 == 23);
|
||||
assert!(c1_bank_meseta.0 == 277);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_withdraw_too_much_meseta() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.meseta = 999980;
|
||||
char1.bank_meseta = 300;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999980)).await.unwrap();
|
||||
entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(300)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -1654,20 +1523,19 @@ async fn test_withdraw_too_much_meseta() {
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let char = characters[0].as_ref().unwrap();
|
||||
assert!(char.meseta == 999980);
|
||||
assert!(char.bank_meseta == 300);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
||||
assert!(c1_meseta.0 == 999980);
|
||||
assert!(c1_bank_meseta.0 == 300);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_withdraw_meseta_inventory_is_maxed() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.meseta = 999999;
|
||||
char1.bank_meseta = 300;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(300)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -1692,8 +1560,8 @@ async fn test_withdraw_meseta_inventory_is_maxed() {
|
||||
unknown: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let char = characters[0].as_ref().unwrap();
|
||||
assert!(char.meseta == 999999);
|
||||
assert!(char.bank_meseta == 300);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap();
|
||||
assert!(c1_meseta.0 == 999999);
|
||||
assert!(c1_bank_meseta.0 == 300);
|
||||
}
|
||||
|
@ -26,9 +26,6 @@ async fn test_equip_unit_from_equip_menu() {
|
||||
evp: 0,
|
||||
slots: 4,
|
||||
}),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
p1_inv.push(entity_gateway.create_item(
|
||||
@ -38,9 +35,6 @@ async fn test_equip_unit_from_equip_menu() {
|
||||
unit: item::unit::UnitType::KnightPower,
|
||||
modifier: None,
|
||||
}),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
p1_inv.push(entity_gateway.create_item(
|
||||
@ -50,9 +44,6 @@ async fn test_equip_unit_from_equip_menu() {
|
||||
unit: item::unit::UnitType::KnightPower,
|
||||
modifier: Some(item::unit::UnitModifier::Plus),
|
||||
}),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
let equipped = item::EquippedEntity {
|
||||
@ -112,9 +103,6 @@ async fn test_unequip_armor_with_units() {
|
||||
evp: 0,
|
||||
slots: 4,
|
||||
}),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
p1_inv.push(entity_gateway.create_item(
|
||||
@ -124,9 +112,6 @@ async fn test_unequip_armor_with_units() {
|
||||
unit: item::unit::UnitType::KnightPower,
|
||||
modifier: None,
|
||||
}),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
p1_inv.push(entity_gateway.create_item(
|
||||
@ -136,9 +121,6 @@ async fn test_unequip_armor_with_units() {
|
||||
unit: item::unit::UnitType::KnightPower,
|
||||
modifier: Some(item::unit::UnitModifier::Plus),
|
||||
}),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
let equipped = item::EquippedEntity {
|
||||
@ -189,9 +171,6 @@ async fn test_sort_items() {
|
||||
evp: 0,
|
||||
slots: 4,
|
||||
}),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
p1_inv.push(entity_gateway.create_item(
|
||||
@ -201,9 +180,6 @@ async fn test_sort_items() {
|
||||
unit: item::unit::UnitType::KnightPower,
|
||||
modifier: None,
|
||||
}),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
p1_inv.push(entity_gateway.create_item(
|
||||
@ -213,9 +189,6 @@ async fn test_sort_items() {
|
||||
unit: item::unit::UnitType::KnightPower,
|
||||
modifier: Some(item::unit::UnitModifier::Plus),
|
||||
}),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
|
||||
|
@ -10,6 +10,77 @@ use libpso::packet::messages::*;
|
||||
mod common;
|
||||
use common::*;
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_pick_up_individual_item() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
|
||||
|
||||
let mut p1_inv = Vec::new();
|
||||
p1_inv.push(entity_gateway.create_item(
|
||||
item::NewItemEntity {
|
||||
item: item::ItemDetail::Weapon(
|
||||
item::weapon::Weapon {
|
||||
weapon: item::weapon::WeaponType::Handgun,
|
||||
grind: 0,
|
||||
special: None,
|
||||
attrs: [None, None, None],
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
}).await.unwrap());
|
||||
|
||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
|
||||
entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
.build());
|
||||
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
|
||||
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
|
||||
|
||||
join_lobby(&mut ship, ClientId(1)).await;
|
||||
join_lobby(&mut ship, ClientId(2)).await;
|
||||
|
||||
create_room(&mut ship, ClientId(1), "room", "").await;
|
||||
join_room(&mut ship, ClientId(2), 0).await;
|
||||
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 1);
|
||||
let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
|
||||
assert_eq!(p2_items.items.len(), 0);
|
||||
|
||||
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
unknown1: 0,
|
||||
map_area: 0,
|
||||
item_id: 0x10000,
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 0);
|
||||
let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
|
||||
assert_eq!(p2_items.items.len(), 0);
|
||||
|
||||
ship.handle(ClientId(2), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
|
||||
client: 0,
|
||||
target: 0,
|
||||
item_id: 0x10000,
|
||||
map_area: 0,
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 0);
|
||||
let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
|
||||
assert_eq!(p2_items.items.len(), 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_pick_up_item_stack_of_items_already_in_inventory() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
@ -25,9 +96,6 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
|
||||
tool: item::tool::ToolType::Monomate
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
let mut p2_items = Vec::new();
|
||||
@ -41,9 +109,6 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
|
||||
tool: tool
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char2.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
p2_items.push(item);
|
||||
@ -107,9 +172,6 @@ async fn test_pick_up_item_stack_of_items_not_already_held() {
|
||||
tool: item::tool::ToolType::Monomate
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char2.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_monomate])).await.unwrap();
|
||||
@ -159,7 +221,7 @@ async fn test_pick_up_meseta_when_inventory_full() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
let (user2, mut char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
|
||||
let (user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
|
||||
|
||||
let mut p1_items = Vec::new();
|
||||
for _ in 0..30usize {
|
||||
@ -174,16 +236,11 @@ async fn test_pick_up_meseta_when_inventory_full() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_items)).await.unwrap();
|
||||
|
||||
char2.meseta = 300;
|
||||
entity_gateway.save_character(&char2).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char2.id, item::Meseta(300)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -225,12 +282,10 @@ async fn test_pick_up_meseta_when_inventory_full() {
|
||||
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(inventory_items.items.len(), 30);
|
||||
|
||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
let characters2 = entity_gateway.get_characters_by_user(&user2).await.unwrap();
|
||||
let c2 = characters2.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
assert!(c1.meseta == 23);
|
||||
assert!(c2.meseta == 277);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap();
|
||||
assert!(c1_meseta.0 == 23);
|
||||
assert!(c2_meseta.0 == 277);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
@ -253,9 +308,6 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap().into());
|
||||
}
|
||||
|
||||
@ -266,9 +318,6 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap()]));
|
||||
|
||||
let mut p2_monomates = Vec::new();
|
||||
@ -279,9 +328,6 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char2.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
|
||||
@ -345,9 +391,6 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -363,9 +406,6 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char2.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
|
||||
@ -425,10 +465,9 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
|
||||
async fn test_can_not_drop_more_meseta_than_is_held() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
|
||||
char1.meseta = 300;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -457,9 +496,8 @@ async fn test_can_not_drop_more_meseta_than_is_held() {
|
||||
})))).await;
|
||||
assert!(split_attempt.is_err());
|
||||
|
||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
assert!(c1.meseta == 300);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert!(c1_meseta.0 == 300);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
@ -478,9 +516,6 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -493,9 +528,6 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char2.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_monomates])).await.unwrap();
|
||||
@ -546,13 +578,11 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
|
||||
async fn test_can_not_pick_up_meseta_when_full() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
let (user2, mut char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
|
||||
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
let (user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
|
||||
|
||||
char1.meseta = 999999;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
char2.meseta = 300;
|
||||
entity_gateway.save_character(&char2).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char2.id, item::Meseta(300)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -592,25 +622,21 @@ async fn test_can_not_pick_up_meseta_when_full() {
|
||||
})))).await.unwrap().collect::<Vec<_>>();
|
||||
assert!(packets.len() == 0);
|
||||
|
||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
let characters2 = entity_gateway.get_characters_by_user(&user2).await.unwrap();
|
||||
let c2 = characters2.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
assert!(c1.meseta == 999999);
|
||||
assert!(c2.meseta == 277);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap();
|
||||
assert!(c1_meseta.0 == 999999);
|
||||
assert!(c2_meseta.0 == 277);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_meseta_caps_at_999999_when_trying_to_pick_up_more() {
|
||||
let mut entity_gateway = InMemoryGateway::default();
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
let (user2, mut char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
|
||||
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
let (user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
|
||||
|
||||
char1.meseta = 999998;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
char2.meseta = 300;
|
||||
entity_gateway.save_character(&char2).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999998)).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char2.id, item::Meseta(300)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -649,12 +675,10 @@ async fn test_meseta_caps_at_999999_when_trying_to_pick_up_more() {
|
||||
unknown: [0; 3]
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
let characters2 = entity_gateway.get_characters_by_user(&user2).await.unwrap();
|
||||
let c2 = characters2.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
assert!(c1.meseta == 999999);
|
||||
assert!(c2.meseta == 277);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap();
|
||||
assert!(c1_meseta.0 == 999999);
|
||||
assert!(c2_meseta.0 == 277);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
@ -673,9 +697,6 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -732,3 +753,20 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
|
||||
vec![item::ItemEntityId(1), item::ItemEntityId(2)]);
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
/*
|
||||
#[async_std::test]
|
||||
async fn test_try_and_pick_up_individual_item_twice() {
|
||||
panic!()
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_try_and_pick_up_stacked_item_twice() {
|
||||
panic!()
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_try_and_pick_up_meseta_twice() {
|
||||
panic!()
|
||||
}
|
||||
*/
|
||||
|
@ -29,9 +29,6 @@ async fn test_use_monomate() {
|
||||
tool: tool
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
p1_items.push(item::InventoryItemEntity::Stacked(item));
|
||||
@ -79,9 +76,6 @@ async fn test_use_monomate_twice() {
|
||||
tool: tool
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
p1_items.push(item::InventoryItemEntity::Stacked(item));
|
||||
@ -132,9 +126,6 @@ async fn test_use_last_monomate() {
|
||||
tool: tool
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap()]));
|
||||
}
|
||||
|
||||
@ -176,9 +167,6 @@ async fn test_use_nonstackable_tool() {
|
||||
tool: item::tool::ToolType::MagicStoneIritista,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
|
||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_items)).await.unwrap();
|
||||
@ -217,9 +205,6 @@ async fn test_use_materials() {
|
||||
tool: tool
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
p1_inv.push(item::InventoryItemEntity::Stacked(item));
|
||||
|
@ -22,10 +22,6 @@ async fn test_mag_feed() {
|
||||
item: item::ItemDetail::Mag(
|
||||
item::mag::Mag::baby_mag(0)
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
//equipped: true,
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
let mut monomates = Vec::new();
|
||||
@ -37,9 +33,6 @@ async fn test_mag_feed() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -108,9 +101,6 @@ async fn test_mag_change_owner() {
|
||||
item: item::ItemDetail::Mag(
|
||||
item::mag::Mag::baby_mag(0)
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![mag])).await.unwrap();
|
||||
@ -169,9 +159,6 @@ async fn test_mag_cell() {
|
||||
item: item::ItemDetail::Mag(
|
||||
item::mag::Mag::baby_mag(0)
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
for _ in 0..1000usize {
|
||||
@ -182,9 +169,6 @@ async fn test_mag_cell() {
|
||||
tool: item::tool::ToolType::Monomate,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::FedToMag {
|
||||
mag: mag.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
entity_gateway.feed_mag(&mag.id, &fed_tool.id).await.unwrap();
|
||||
}
|
||||
@ -195,9 +179,6 @@ async fn test_mag_cell() {
|
||||
tool: item::tool::ToolType::CellOfMag502,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
let equipped = item::EquippedEntity {
|
||||
|
@ -31,9 +31,6 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
@ -50,9 +47,6 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
|
||||
tekked: true,
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char2.id,
|
||||
}
|
||||
}).await.unwrap());
|
||||
}
|
||||
|
||||
|
@ -107,8 +107,8 @@ async fn test_player_buys_from_weapon_shop() {
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.exp = 80000000;
|
||||
char1.meseta = 999999;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -132,9 +132,8 @@ async fn test_player_buys_from_weapon_shop() {
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
assert!(c1.meseta < 999999);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert!(c1_meseta.0 < 999999);
|
||||
//let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 1);
|
||||
@ -146,8 +145,8 @@ async fn test_player_buys_from_tool_shop() {
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.exp = 80000000;
|
||||
char1.meseta = 999999;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -171,9 +170,8 @@ async fn test_player_buys_from_tool_shop() {
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
assert!(c1.meseta < 999999);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert!(c1_meseta.0 < 999999);
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 1);
|
||||
}
|
||||
@ -184,8 +182,8 @@ async fn test_player_buys_multiple_from_tool_shop() {
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.exp = 80000000;
|
||||
char1.meseta = 999999;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -209,9 +207,8 @@ async fn test_player_buys_multiple_from_tool_shop() {
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
assert!(c1.meseta < 999999);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert!(c1_meseta.0 < 999999);
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 1);
|
||||
p1_items.items[0].with_stacked(|item| {
|
||||
@ -226,8 +223,8 @@ async fn test_player_buys_from_armor_shop() {
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.exp = 80000000;
|
||||
char1.meseta = 999999;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -251,9 +248,8 @@ async fn test_player_buys_from_armor_shop() {
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
assert!(c1.meseta < 999999);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert!(c1_meseta.0 < 999999);
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 1);
|
||||
}
|
||||
@ -269,7 +265,7 @@ async fn test_other_clients_see_purchase() {
|
||||
let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
|
||||
char1.exp = 80000000;
|
||||
char1.meseta = 999999;
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
@ -312,8 +308,8 @@ async fn test_other_clients_see_stacked_purchase() {
|
||||
let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
|
||||
char1.exp = 80000000;
|
||||
char1.meseta = 999999;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
entity_gateway.create_item(
|
||||
item::NewItemEntity {
|
||||
item: item::ItemDetail::Tool(
|
||||
@ -321,9 +317,6 @@ async fn test_other_clients_see_stacked_purchase() {
|
||||
tool: item::tool::ToolType::Monomate
|
||||
}
|
||||
),
|
||||
location: item::ItemLocation::Inventory {
|
||||
character_id: char1.id,
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
@ -388,9 +381,8 @@ async fn test_buying_item_without_enough_mseseta() {
|
||||
})))).await;
|
||||
|
||||
assert!(packets.is_err());
|
||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
assert_eq!(c1.meseta, 0);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert_eq!(c1_meseta.0, 0);
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 0);
|
||||
}
|
||||
@ -401,8 +393,8 @@ async fn test_player_double_buys_from_tool_shop() {
|
||||
|
||||
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.exp = 80000000;
|
||||
char1.meseta = 999999;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -444,9 +436,8 @@ async fn test_player_double_buys_from_tool_shop() {
|
||||
unknown1: 0,
|
||||
})))).await.unwrap().for_each(drop);
|
||||
|
||||
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
|
||||
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
|
||||
assert!(c1.meseta < 999999);
|
||||
let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
|
||||
assert!(c1_meseta.0 < 999999);
|
||||
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
|
||||
assert_eq!(p1_items.items.len(), 2);
|
||||
p1_items.items[0].with_stacked(|item| {
|
||||
@ -467,8 +458,8 @@ async fn test_techs_disappear_from_shop_when_bought() {
|
||||
|
||||
let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.exp = 80000000;
|
||||
char1.meseta = 999999;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
@ -529,8 +520,8 @@ async fn test_units_disappear_from_shop_when_bought() {
|
||||
|
||||
let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
|
||||
char1.exp = 80000000;
|
||||
char1.meseta = 999999;
|
||||
entity_gateway.save_character(&char1).await.unwrap();
|
||||
entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap();
|
||||
|
||||
let mut ship = Box::new(ShipServerState::builder()
|
||||
.gateway(entity_gateway.clone())
|
||||
|
4382
tests/test_trade.rs
Normal file
4382
tests/test_trade.rs
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user