Browse Source

trade refactor

pull/113/head
jake 2 years ago
parent
commit
6631468871
  1. 1
      Cargo.toml
  2. 4
      src/entity/gateway/entitygateway.rs
  3. 21
      src/entity/gateway/inmemory.rs
  4. 10
      src/entity/gateway/postgres/models.rs
  5. 11
      src/entity/item/mod.rs
  6. 301
      src/ship/items/actions.rs
  7. 142
      src/ship/packet/handler/trade.rs
  8. 10
      src/ship/ship.rs
  9. 10
      src/ship/trade.rs
  10. 433
      tests/test_trade.rs

1
Cargo.toml

@ -25,6 +25,7 @@ derive_more = { version = "0.99.3", features = ["display"]}
thiserror = "1.0.15" thiserror = "1.0.15"
ages-prs = "0.1" ages-prs = "0.1"
async-trait = "0.1.51" async-trait = "0.1.51"
async-recursion= "1.0.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
barrel = { version = "0.6.5", features = ["pg"] } barrel = { version = "0.6.5", features = ["pg"] }
refinery = { version = "0.5.0", features = ["postgres"] } refinery = { version = "0.5.0", features = ["postgres"] }

4
src/entity/gateway/entitygateway.rs

@ -143,6 +143,10 @@ pub trait EntityGateway: Send + Sync {
async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: &BankName, _amount: Meseta) -> Result<(), GatewayError> { async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: &BankName, _amount: Meseta) -> Result<(), GatewayError> {
unimplemented!(); unimplemented!();
} }
async fn create_trade(&mut self, _char_id1: &CharacterEntityId, _char_id2: &CharacterEntityId) -> Result<TradeEntity, GatewayError> {
unimplemented!();
}
} }

21
src/entity/gateway/inmemory.rs

@ -56,6 +56,9 @@ impl<'a> EntityGatewayTransaction for InMemoryGatewayTransaction<'a> {
self.original_gateway.weapon_modifiers.lock().unwrap().clear(); self.original_gateway.weapon_modifiers.lock().unwrap().clear();
self.original_gateway.weapon_modifiers.lock().unwrap().extend(self.working_gateway.weapon_modifiers.lock().unwrap().clone()); self.original_gateway.weapon_modifiers.lock().unwrap().extend(self.working_gateway.weapon_modifiers.lock().unwrap().clone());
self.original_gateway.trades.lock().unwrap().clear();
self.original_gateway.trades.lock().unwrap().extend(self.working_gateway.trades.lock().unwrap().clone());
Ok(()) Ok(())
} }
} }
@ -73,6 +76,7 @@ pub struct InMemoryGateway {
equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>, equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>,
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>, mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>, weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>,
trades: Arc<Mutex<Vec<TradeEntity>>>,
} }
impl Default for InMemoryGateway { impl Default for InMemoryGateway {
@ -89,6 +93,7 @@ impl Default for InMemoryGateway {
equips: Arc::new(Mutex::new(BTreeMap::new())), equips: Arc::new(Mutex::new(BTreeMap::new())),
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())), mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())), weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
trades: Arc::new(Mutex::new(Vec::new())),
} }
} }
} }
@ -165,6 +170,7 @@ impl EntityGateway for InMemoryGateway {
let equips = self.equips.lock().unwrap().clone(); let equips = self.equips.lock().unwrap().clone();
let mag_modifiers = self.mag_modifiers.lock().unwrap().clone(); let mag_modifiers = self.mag_modifiers.lock().unwrap().clone();
let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone(); let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone();
let trades = self.trades.lock().unwrap().clone();
InMemoryGateway { InMemoryGateway {
users: Arc::new(Mutex::new(users)), users: Arc::new(Mutex::new(users)),
@ -178,6 +184,7 @@ impl EntityGateway for InMemoryGateway {
equips: Arc::new(Mutex::new(equips)), equips: Arc::new(Mutex::new(equips)),
mag_modifiers: Arc::new(Mutex::new(mag_modifiers)), mag_modifiers: Arc::new(Mutex::new(mag_modifiers)),
weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)), weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)),
trades: Arc::new(Mutex::new(trades)),
} }
}; };
@ -206,6 +213,7 @@ impl EntityGateway for InMemoryGateway {
let equips = self.equips.lock().unwrap().clone(); let equips = self.equips.lock().unwrap().clone();
let mag_modifiers = self.mag_modifiers.lock().unwrap().clone(); let mag_modifiers = self.mag_modifiers.lock().unwrap().clone();
let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone(); let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone();
let trades = self.trades.lock().unwrap().clone();
let working_gateway = InMemoryGateway { let working_gateway = InMemoryGateway {
users: Arc::new(Mutex::new(users)), users: Arc::new(Mutex::new(users)),
@ -219,6 +227,7 @@ impl EntityGateway for InMemoryGateway {
equips: Arc::new(Mutex::new(equips)), equips: Arc::new(Mutex::new(equips)),
mag_modifiers: Arc::new(Mutex::new(mag_modifiers)), mag_modifiers: Arc::new(Mutex::new(mag_modifiers)),
weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)), weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)),
trades: Arc::new(Mutex::new(trades)),
}; };
let transaction = Box::new(InMemoryGatewayTransaction { let transaction = Box::new(InMemoryGatewayTransaction {
@ -482,4 +491,16 @@ impl EntityGateway for InMemoryGateway {
Err(GatewayError::Error) Err(GatewayError::Error)
} }
} }
async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result<TradeEntity, GatewayError> {
let mut trades = self.trades.lock().unwrap();
let id = trades.len() as u32;
let new_trade = TradeEntity {
id: TradeId(id),
character1: *char_id1,
character2: *char_id2,
};
trades.push(new_trade.clone());
Ok(new_trade)
}
} }

10
src/entity/gateway/postgres/models.rs

@ -606,7 +606,7 @@ pub enum PgItemNoteDetail {
}, },
SoldToShop, SoldToShop,
Trade { Trade {
id: u32,
trade_id: u32,
character_to: u32, character_to: u32,
character_from: u32, character_from: u32,
}, },
@ -647,8 +647,8 @@ impl From<ItemNote> for PgItemNoteDetail {
character_id: character_id.0, character_id: character_id.0,
}, },
ItemNote::SoldToShop => PgItemNoteDetail::SoldToShop, ItemNote::SoldToShop => PgItemNoteDetail::SoldToShop,
ItemNote::Trade{id, character_to, character_from} => PgItemNoteDetail::Trade {
id: id.0,
ItemNote::Trade{trade_id, character_to, character_from} => PgItemNoteDetail::Trade {
trade_id: trade_id.0,
character_to: character_to.0, character_to: character_to.0,
character_from: character_from.0, character_from: character_from.0,
}, },
@ -695,8 +695,8 @@ impl From<PgItemNoteDetail> for ItemNote {
character_id: CharacterEntityId(character_id), character_id: CharacterEntityId(character_id),
}, },
PgItemNoteDetail::SoldToShop => ItemNote::SoldToShop, PgItemNoteDetail::SoldToShop => ItemNote::SoldToShop,
PgItemNoteDetail::Trade {id, character_to, character_from} => ItemNote::Trade {
id: TradeId(id as u32),
PgItemNoteDetail::Trade {trade_id, character_to, character_from} => ItemNote::Trade {
trade_id: TradeId(trade_id as u32),
character_to: CharacterEntityId(character_to as u32), character_to: CharacterEntityId(character_to as u32),
character_from: CharacterEntityId(character_from as u32), character_from: CharacterEntityId(character_from as u32),
}, },

11
src/entity/item/mod.rs

@ -19,7 +19,7 @@ pub struct ItemEntityId(pub u32);
pub struct ItemId(u32); pub struct ItemId(u32);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, derive_more::Display)] #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, derive_more::Display)]
pub struct BankName(pub String); pub struct BankName(pub String);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct TradeId(pub u32); pub struct TradeId(pub u32);
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -56,7 +56,7 @@ pub enum ItemNote {
}, },
SoldToShop, SoldToShop,
Trade { Trade {
id: TradeId,
trade_id: TradeId,
character_to: CharacterEntityId, character_to: CharacterEntityId,
character_from: CharacterEntityId, character_from: CharacterEntityId,
}, },
@ -327,3 +327,10 @@ impl BankEntity {
} }
} }
} }
#[derive(Clone, Debug)]
pub struct TradeEntity {
pub id: TradeId,
pub character1: CharacterEntityId,
pub character2: CharacterEntityId,
}

301
src/ship/items/actions.rs

@ -9,11 +9,10 @@ use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction};
use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail,
StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail}; StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail};
use crate::ship::items::apply_item::apply_item; use crate::ship::items::apply_item::apply_item;
use crate::entity::item::{ItemDetail, ItemEntity, NewItemEntity};
use crate::entity::item::tool::Tool;
use crate::entity::item::{ItemDetail, NewItemEntity, TradeId};
use crate::ship::shops::ShopItem; use crate::ship::shops::ShopItem;
use crate::ship::trade::TradeItem;
use crate::ship::location::{AreaClient, RoomId};
pub enum TriggerCreateItem { pub enum TriggerCreateItem {
Yes, Yes,
@ -24,7 +23,7 @@ fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()) -> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>> -> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), FloorItem), ItemStateError>> + Send + 'a>>
{ {
move |(mut item_state, transaction), _| {
move |(mut item_state, transaction): (ItemStateProxy<'_>, Box<dyn EntityGatewayTransaction + '_>) , _| {
Box::pin(async move { Box::pin(async move {
let mut floor = item_state.floor(&character_id)?; let mut floor = item_state.floor(&character_id)?;
let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?;
@ -200,6 +199,23 @@ fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32)
let mut inventory = item_state.inventory(&character_id)?; let mut inventory = item_state.inventory(&character_id)?;
inventory.remove_meseta(amount)?; inventory.remove_meseta(amount)?;
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
item_state.set_inventory(inventory);
Ok(((item_state, transaction), ()))
})
}
}
fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
move |(mut item_state, mut transaction), _| {
Box::pin(async move {
let mut inventory = item_state.inventory(&character_id)?;
inventory.add_meseta(amount)?;
transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?;
item_state.set_inventory(inventory);
Ok(((item_state, transaction), ())) Ok(((item_state, transaction), ()))
}) })
@ -419,6 +435,8 @@ where
let item_state_proxy = ItemStateProxy::new(item_state); let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default() let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(take_item_from_bank(character.id, *item_id, amount)) .act(take_item_from_bank(character.id, *item_id, amount))
//.act(bank_item_to_inventory_item)
//.act(add_item_to_inventory)
.act(add_bank_item_to_inventory(&character)) .act(add_bank_item_to_inventory(&character))
.commit((item_state_proxy, transaction)) .commit((item_state_proxy, transaction))
.await?; .await?;
@ -780,6 +798,8 @@ where
let item_state_proxy = ItemStateProxy::new(item_state); let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), result) = ItemStateAction::default() let ((item_state_proxy, transaction), result) = ItemStateAction::default()
.act(take_meseta_from_inventory(character.id, item_price)) .act(take_meseta_from_inventory(character.id, item_price))
//.act(bought_item_to_inventory_item)
//.act(add_item_to_inventory)
.act(add_bought_item_to_inventory(character.id, shop_item, item_id, amount)) .act(add_bought_item_to_inventory(character.id, shop_item, item_id, amount))
.commit((item_state_proxy, transaction)) .commit((item_state_proxy, transaction))
.await?; .await?;
@ -832,3 +852,274 @@ where
Ok((transaction, result)) Ok((transaction, result))
}).await }).await
} }
#[async_recursion::async_recursion]
async fn iterate_inner<'a, I, O, T, F, FR>(state: (ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>),
mut input: Vec<I>,
func: F,
arg: T)
-> Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>
where
'a: 'async_recursion,
I: Send,
O: Send,
T: Clone + Send + Sync,
F: Fn(I) -> FR + Send + Sync + Clone + 'static,
FR: Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync,
{
let item = match input.pop() {
Some(item) => item,
None => return Ok((state, Vec::new()))
};
let (state, mut output) = iterate_inner(state, input, func.clone(), arg.clone()).await.unwrap();
let rfunc = func(item);
let (state, result) = rfunc(state, arg.clone()).await.unwrap();
output.push(result);
Ok((state, output))
}
pub fn iterate<'k, I, O, T, F, FR>(
input: Vec<I>,
func: F)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>> + Send + 'a>>
where
O: Send,
I: Send + Clone + 'static + std::fmt::Debug,
T: Send + Clone + 'static + std::fmt::Debug,
F: Fn(I) -> FR + Send + Sync + Clone + 'static,
FR: for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync,
T: Clone + Send + Sync,
{
move |(mut item_state, mut transaction), arg| {
let input = input.clone();
let func = func.clone();
println!("i {:?} {:?}", input, arg);
Box::pin(async move {
let (state, result) = iterate_inner((item_state, transaction), input, func, arg.clone()).await?;
Ok((state, result))
})
}
}
#[async_recursion::async_recursion]
async fn foreach_inner<'a, O, T, F>(state: (ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>),
mut input: Vec<T>,
func: F)
-> Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>
where
'a: 'async_recursion,
O: Send,
T: Clone + Send,
F: Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync,
F: Clone,
{
let item = match input.pop() {
Some(item) => item,
None => return Ok((state, Vec::new()))
};
let (state, mut output) = foreach_inner(state, input, func.clone()).await?;
let (state, result) = func(state, item).await?;
output.push(result);
Ok((state, output))
}
pub fn foreach<'k, O, T, F>(func: F)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<T>)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<O>), ItemStateError>> + Send + 'a>>
where
O: Send,
T: Send + Clone + 'static + std::fmt::Debug,
F: for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), O), ItemStateError>> + Send + 'a>> + Send + Sync + 'static,
F: Clone,
T: Clone + Send + Sync,
{
move |(item_state, transaction), items| {
println!("fe {:?}", items);
let func = func.clone();
Box::pin(async move {
let (state, result) = foreach_inner((item_state, transaction), items, func).await?;
Ok((state, result))
})
}
}
fn clear<'a, T: Send + Clone + 'a>()
-> impl Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ()), ItemStateError>> + Send + 'a>>
{
move |state, _| {
Box::pin(async move {
Ok((state, ()))
})
}
}
fn insert<'a, T: Send + Clone + 'a>(element: T)
-> impl Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), ())
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), T), ItemStateError>> + Send + 'a>>
{
move |state, _| {
let element = element.clone();
Box::pin(async move {
Ok((state, element))
})
}
}
fn add_item_to_inventory(character: CharacterEntity)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> + Clone
{
let character = character.clone();
move |(mut item_state, transaction), inventory_item| {
let character = character.clone();
Box::pin(async move {
//let bank_name = item_state.bank(&character.id)?.name;
let mut inventory = item_state.inventory(&character.id)?;
let character_id = character.id;
/*
let transaction = bank_item.with_entity_id(transaction, |mut transaction, entity_id| {
let bank_name = bank_name.clone();
async move {
transaction.gateway().add_item_note(&entity_id, ItemNote::Withdraw {
character_id,
bank: bank_name,
}).await?;
Ok(transaction)
}}).await?;
*/
let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| {
let character = character.clone();
async move {
transaction.gateway().change_mag_owner(&entity_id, &character).await?;
Ok(transaction)
}}).await?;
inventory.add_item(inventory_item.clone())?;
transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
item_state.set_inventory(inventory);
Ok(((item_state, transaction), inventory_item))
})
}
}
fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_from: CharacterEntityId)
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<InventoryItem>)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), Vec<InventoryItem>), ItemStateError>> + Send + 'a>> + Clone
{
move |(item_state, mut transaction), traded_items| {
Box::pin(async move {
for item in &traded_items {
transaction = item.with_entity_id(transaction, |mut transaction, entity_id| {
async move {
transaction.gateway().add_item_note(&entity_id, ItemNote::Trade {
trade_id,
character_to,
character_from,
}).await?;
Ok(transaction)
}}).await?;
}
Ok(((item_state, transaction), traded_items))
})
}
}
fn assign_new_item_id()
-> impl for<'a> Fn((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem)
-> Pin<Box<dyn Future<Output=Result<((ItemStateProxy<'a>, Box<dyn EntityGatewayTransaction + 'a>), InventoryItem), ItemStateError>> + Send + 'a>> + Clone
{
move |(mut item_state, transaction), mut inventory_item| {
Box::pin(async move {
inventory_item.item_id = item_state.new_item_id()?;
Ok(((item_state, transaction), inventory_item))
})
}
}
pub async fn trade_items<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
room_id: RoomId,
p1: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta),
p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>, Meseta))
-> Result<(Vec<InventoryItem>, Vec<InventoryItem>), ItemStateError>
where
EG: EntityGateway,
{
let p1_trade_items = p1.2
.iter()
.map(|item| {
match item {
TradeItem::Individual(item_id) => (*item_id, 1),
TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32),
}
})
.collect();
let p2_trade_items = p2.2
.iter()
.map(|item| {
match item {
TradeItem::Individual(item_id) => (*item_id, 1),
TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32),
}
})
.collect();
entity_gateway.with_transaction(|mut transaction| async move {
let p1_id = p1.1.id;
let p2_id = p2.1.id;
let trade = transaction.gateway().create_trade(&p1_id, &p2_id).await?;
let item_state_proxy = ItemStateProxy::new(item_state);
let ((item_state_proxy, transaction), p1_removed_items) = ItemStateAction::default()
.act(iterate(p1_trade_items, move |p1_trade_item| take_item_from_inventory(p1_id, p1_trade_item.0, p1_trade_item.1) ))
.act(foreach(assign_new_item_id()))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), p2_removed_items) = ItemStateAction::default()
.act(iterate(p2_trade_items, move |p2_trade_item| take_item_from_inventory(p2_id, p2_trade_item.0, p2_trade_item.1) ))
.act(foreach(assign_new_item_id()))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), p2_new_items) = ItemStateAction::default()
.act(insert(p1_removed_items))
.act(foreach(add_item_to_inventory(p2.1.clone())))
.act(record_trade(trade.id, p1_id, p2_id))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), p1_new_items) = ItemStateAction::default()
.act(insert(p2_removed_items))
.act(foreach(add_item_to_inventory(p1.1.clone())))
.act(record_trade(trade.id, p2_id, p1_id))
.commit((item_state_proxy, transaction))
.await?;
let ((item_state_proxy, transaction), _) = ItemStateAction::default()
.act(take_meseta_from_inventory(p1_id, p1.3.0))
.act(take_meseta_from_inventory(p2_id, p2.3.0))
.act(add_meseta_to_inventory(p1_id, p2.3.0))
.act(add_meseta_to_inventory(p2_id, p1.3.0))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit();
Ok((transaction, (p1_new_items, p2_new_items)))
}).await
}

142
src/ship/packet/handler/trade.rs

@ -4,16 +4,20 @@ use libpso::packet::messages::*;
use crate::common::serverstate::ClientId; use crate::common::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, ShipError, Clients}; use crate::ship::ship::{SendShipPacket, ShipError, Clients};
use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, ItemToTradeDetail};
use crate::ship::items::inventory::InventoryItem;
use crate::ship::items::ClientItemId;
use crate::ship::items::state::{ItemState, ItemStateError, InventoryItemDetail};
use crate::ship::trade::{TradeItem, TradeState, TradeStatus}; use crate::ship::trade::{TradeItem, TradeState, TradeStatus};
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use crate::ship::packet::builder; use crate::ship::packet::builder;
use crate::ship::items::actions::trade_items;
use crate::entity::item::ItemDetail;
use crate::entity::item::tool::Tool;
use crate::ship::location::AreaClient;
use crate::entity::item::{Meseta, ItemNote};
pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01); pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01);
pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF); pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF);
#[derive(thiserror::Error, Debug, PartialEq, Eq)] #[derive(thiserror::Error, Debug, PartialEq, Eq)]
pub enum TradeError { pub enum TradeError {
#[error("no partner")] #[error("no partner")]
@ -51,9 +55,9 @@ pub async fn trade_request(id: ClientId,
target: u32, target: u32,
client_location: &ClientLocation, client_location: &ClientLocation,
clients: &mut Clients, clients: &mut Clients,
item_manager: &mut ItemManager,
item_state: &mut ItemState,
trades: &mut TradeState) trades: &mut TradeState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
{ {
let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet
match trade_request.trade { match trade_request.trade {
@ -114,18 +118,18 @@ pub async fn trade_request(id: ClientId,
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> { .with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading { if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?; let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
let inventory = item_manager.get_character_inventory(&client.character)?;
let inventory = item_state.get_character_inventory(&client.character)?;
if ClientItemId(item_id) == MESETA_ITEM_ID { if ClientItemId(item_id) == MESETA_ITEM_ID {
this.meseta += amount as usize; this.meseta += amount as usize;
} }
else { else {
let item = inventory.get_item_by_id(ClientItemId(item_id)).ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item_id)))?;
let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?;
match item {
InventoryItem::Individual(_) => {
match &item.item {
InventoryItemDetail::Individual(_) => {
this.items.push(TradeItem::Individual(ClientItemId(item_id))); this.items.push(TradeItem::Individual(ClientItemId(item_id)));
}, },
InventoryItem::Stacked(stacked_item) => {
InventoryItemDetail::Stacked(stacked_item) => {
if stacked_item.count() < amount as usize { if stacked_item.count() < amount as usize {
return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into()); return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into());
} }
@ -160,20 +164,20 @@ pub async fn trade_request(id: ClientId,
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> { .with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading { if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
let client = clients.get(&this.client())?; //.ok_or(ShipError::ClientNotFound(id)).ok()?; let client = clients.get(&this.client())?; //.ok_or(ShipError::ClientNotFound(id)).ok()?;
let inventory = item_manager.get_character_inventory(&client.character).ok()?;
let inventory = item_state.get_character_inventory(&client.character).ok()?;
if ClientItemId(item_id) == MESETA_ITEM_ID { if ClientItemId(item_id) == MESETA_ITEM_ID {
this.meseta -= amount as usize; this.meseta -= amount as usize;
} }
else { else {
let item = inventory.get_item_by_id(ClientItemId(item_id))?;
let item = inventory.get_by_client_id(&ClientItemId(item_id))?;
match item {
InventoryItem::Individual(_) => {
match &item.item {
InventoryItemDetail::Individual(_) => {
this.items.retain(|item| { this.items.retain(|item| {
item.item_id() != ClientItemId(item_id) item.item_id() != ClientItemId(item_id)
}) })
}, },
InventoryItem::Stacked(_stacked_item) => {
InventoryItemDetail::Stacked(_stacked_item) => {
let trade_item_index = this.items.iter() let trade_item_index = this.items.iter()
.position(|item| { .position(|item| {
item.item_id() == ClientItemId(item_id) item.item_id() == ClientItemId(item_id)
@ -293,7 +297,7 @@ pub async fn inner_items_to_trade(id: ClientId,
items_to_trade: &ItemsToTrade, items_to_trade: &ItemsToTrade,
client_location: &ClientLocation, client_location: &ClientLocation,
clients: &mut Clients, clients: &mut Clients,
item_manager: &mut ItemManager,
item_state: &mut ItemState,
trades: &mut TradeState) trades: &mut TradeState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
{ {
@ -305,7 +309,7 @@ pub async fn inner_items_to_trade(id: ClientId,
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?; 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 other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?;
let inventory = item_manager.get_character_inventory(&client.character)?;
let inventory = item_state.get_character_inventory(&client.character)?;
if items_to_trade.count as usize != (this.items.len() + (if this.meseta != 0 { 1 } else { 0 })) { if items_to_trade.count as usize != (this.items.len() + (if this.meseta != 0 { 1 } else { 0 })) {
return Err(TradeError::MismatchedTradeItems.into()) return Err(TradeError::MismatchedTradeItems.into())
@ -320,8 +324,8 @@ pub async fn inner_items_to_trade(id: ClientId,
return Err(TradeError::InvalidItemId(ClientItemId(item.item_id)).into()) return Err(TradeError::InvalidItemId(ClientItemId(item.item_id)).into())
} }
let amount = u32::from_le_bytes(item.item_data2); 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)?;
let character_meseta = item_state.get_character_inventory(&client.character).map_err(|_| TradeError::InvalidMeseta)?.meseta;
let other_character_meseta = item_state.get_character_inventory(&other_client.character).map_err(|_| TradeError::InvalidMeseta)?.meseta;
if amount > character_meseta.0 { if amount > character_meseta.0 {
return Err(TradeError::InvalidMeseta.into()) return Err(TradeError::InvalidMeseta.into())
} }
@ -334,8 +338,8 @@ pub async fn inner_items_to_trade(id: ClientId,
Ok(()) Ok(())
} }
else { else {
let real_item = inventory.get_item_by_id(ClientItemId(item.item_id))
.ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?;
let real_item = inventory.get_by_client_id(&ClientItemId(item.item_id))
.ok_or(ItemStateError::InvalidItemId(ClientItemId(item.item_id)))?;
let real_trade_item = this.items let real_trade_item = this.items
.iter() .iter()
.find(|i| i.item_id() == ClientItemId(item.item_id)) .find(|i| i.item_id() == ClientItemId(item.item_id))
@ -345,28 +349,28 @@ pub async fn inner_items_to_trade(id: ClientId,
.cloned().collect::<Vec<u8>>() .cloned().collect::<Vec<u8>>()
.try_into() .try_into()
.unwrap(); .unwrap();
match real_item {
InventoryItem::Individual(_individual_inventory_item) => {
if real_item.as_client_bytes() == trade_item_bytes {
match &real_item.item {
InventoryItemDetail::Individual(_individual_inventory_item) => {
if real_item.item.as_client_bytes() == trade_item_bytes {
Ok(()) Ok(())
} }
else { else {
Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into()) 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] {
InventoryItemDetail::Stacked(stacked_inventory_item) => {
if real_item.item.as_client_bytes()[0..4] == trade_item_bytes[0..4] {
let amount = trade_item_bytes[5] as usize; let amount = trade_item_bytes[5] as usize;
if amount <= stacked_inventory_item.entity_ids.len() { if amount <= stacked_inventory_item.entity_ids.len() {
if real_trade_item.stacked().ok_or(TradeError::SketchyTrade)?.1 == amount { if real_trade_item.stacked().ok_or(TradeError::SketchyTrade)?.1 == amount {
Ok(()) Ok(())
} }
else { else {
Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into())
Err(TradeError::InvalidStackAmount(real_item.item_id, amount).into())
} }
} }
else { else {
Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into())
Err(TradeError::InvalidStackAmount(real_item.item_id, amount).into())
} }
} }
else { else {
@ -405,11 +409,11 @@ pub async fn items_to_trade(id: ClientId,
items_to_trade_pkt: &ItemsToTrade, items_to_trade_pkt: &ItemsToTrade,
client_location: &ClientLocation, client_location: &ClientLocation,
clients: &mut Clients, clients: &mut Clients,
item_manager: &mut ItemManager,
item_state: &mut ItemState,
trades: &mut TradeState) trades: &mut TradeState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> -> 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;
let t = inner_items_to_trade(id, items_to_trade_pkt, client_location, clients, item_state, trades).await;
match t { match t {
Ok(p) => Ok(p), Ok(p) => Ok(p),
Err(err) => { Err(err) => {
@ -429,7 +433,7 @@ pub async fn trade_confirmed<EG>(id: ClientId,
entity_gateway: &mut EG, entity_gateway: &mut EG,
client_location: &ClientLocation, client_location: &ClientLocation,
clients: &mut Clients, clients: &mut Clients,
item_manager: &mut ItemManager,
item_state: &mut ItemState,
trades: &mut TradeState) trades: &mut TradeState)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
where where
@ -473,36 +477,63 @@ where
Ok(Box::new(None.into_iter()) as Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>) 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)) => { 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,
let remove_item_packets = this.items
.clone()
.into_iter()
.map(move |item| {
(this_local_client, item)
})
.chain(other.items
.clone()
.into_iter()
.map(move |item| {
(other_local_client, item)
}))
.map(|(client, item)| {
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(client, item.item_id(), item.amount() as u32))
});
let (this_new_items, other_new_items) = trade_items(item_state,
entity_gateway,
room_id, room_id,
(&this_local_client, &this_client.character, &this.items, this.meseta),
(&other_local_client, &other_client.character, &other.items, other.meseta)).await?;
(&this_local_client, &this_client.character, &this.items, Meseta(this.meseta as u32)),
(&other_local_client, &other_client.character, &other.items, Meseta(other.meseta as u32))).await?;
let clients_in_room = client_location.get_all_clients_by_client(id)?;
let traded_item_packets = traded_items
let create_item_packets = this_new_items
.into_iter() .into_iter()
.flat_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))
]
.map(move |item| {
(this_local_client, item)
})
.chain(other_new_items
.into_iter()
.map(move |item| {
(other_local_client, item)
}))
.map(|(client, item)| {
match item.item {
InventoryItemDetail::Individual(individual_item) => {
GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item.item).unwrap())
}, },
ItemToTradeDetail::Meseta(amount) => {
InventoryItemDetail::Stacked(stacked_item) => {
GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap())
}
}
});
let meseta_packets = vec![(this_local_client, other_local_client, this.meseta), (other_local_client, this_local_client, other.meseta)]
.into_iter()
.filter(|(_, _, meseta)| *meseta != 0)
.flat_map(|(this, other, meseta)| {
[ [
GameMessage::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))
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(this, MESETA_ITEM_ID, meseta as u32)),
GameMessage::CreateItem(builder::message::create_meseta(other, meseta)),
] ]
},
}
})
});
let clients_in_room = client_location.get_all_clients_by_client(id)?;
let traded_item_packets = remove_item_packets
.chain(create_item_packets)
.chain(meseta_packets)
.flat_map(move |packet| { .flat_map(move |packet| {
clients_in_room clients_in_room
.clone() .clone()
@ -521,6 +552,7 @@ where
} }
}) })
}); });
let close_trade = vec![ let close_trade = vec![
(this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())), (this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())),
(other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())) (other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default()))

10
src/ship/ship.rs

@ -78,8 +78,10 @@ pub enum ShipError {
InvalidShip(usize), InvalidShip(usize),
InvalidBlock(usize), InvalidBlock(usize),
InvalidItem(items::ClientItemId), InvalidItem(items::ClientItemId),
#[error("tradeerror {0}")]
#[error("trade error {0}")]
TradeError(#[from] crate::ship::packet::handler::trade::TradeError), TradeError(#[from] crate::ship::packet::handler::trade::TradeError),
#[error("trade state error {0}")]
TradeStateError(#[from] crate::ship::trade::TradeStateError),
} }
#[derive(Debug)] #[derive(Debug)]
@ -569,7 +571,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? 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) => { 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?
handler::trade::trade_request(id, trade_request, target, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await?
}, },
_ => { _ => {
let cmsg = msg.clone(); let cmsg = msg.clone();
@ -744,11 +746,11 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
}, },
RecvShipPacket::ItemsToTrade(items_to_trade) => { RecvShipPacket::ItemsToTrade(items_to_trade) => {
let block = self.blocks.with_client(id, &self.clients)?; 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?
handler::trade::items_to_trade(id, items_to_trade, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await?
}, },
RecvShipPacket::TradeConfirmed(_) => { RecvShipPacket::TradeConfirmed(_) => {
let block = self.blocks.with_client(id, &self.clients)?; 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?
handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await?
}, },
RecvShipPacket::KeyboardConfig(keyboard_config) => { RecvShipPacket::KeyboardConfig(keyboard_config) => {
handler::settings::keyboard_config(id, keyboard_config, &mut self.clients, &mut self.entity_gateway).await handler::settings::keyboard_config(id, keyboard_config, &mut self.clients, &mut self.entity_gateway).await

10
src/ship/trade.rs

@ -31,6 +31,13 @@ impl TradeItem {
TradeItem::Stacked(item_id, _) => *item_id, TradeItem::Stacked(item_id, _) => *item_id,
} }
} }
pub fn amount(&self) -> usize {
match self {
TradeItem::Individual(_) => 1,
TradeItem::Stacked(_, amount) => *amount,
}
}
} }
@ -67,9 +74,10 @@ impl ClientTradeState {
} }
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
#[error("")]
pub enum TradeStateError { pub enum TradeStateError {
#[error("client not in trade {0}")]
ClientNotInTrade(ClientId), ClientNotInTrade(ClientId),
#[error("mismatched trade {0} {1}")]
MismatchedTrade(ClientId, ClientId), MismatchedTrade(ClientId, ClientId),
} }

433
tests/test_trade.rs

@ -2,10 +2,11 @@ use std::convert::TryInto;
use elseware::common::serverstate::{ClientId, ServerState}; use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::item; use elseware::entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError};
use elseware::entity::item::{Meseta, ItemEntity}; use elseware::entity::item::{Meseta, ItemEntity};
use elseware::ship::items::transaction::TransactionError; use elseware::ship::items::transaction::TransactionError;
use elseware::ship::packet::handler::trade::TradeError; use elseware::ship::packet::handler::trade::TradeError;
use elseware::ship::items::state::{ItemStateError, InventoryError};
use libpso::packet::ship::*; use libpso::packet::ship::*;
use libpso::packet::messages::*; use libpso::packet::messages::*;
@ -190,16 +191,16 @@ async fn test_trade_one_individual_item() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}), msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
@ -292,15 +293,15 @@ async fn test_trade_player2_to_player1() {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}), msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
@ -392,16 +393,16 @@ async fn test_reverse_trade_ack_order() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}), msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
@ -496,16 +497,16 @@ async fn test_trade_one_stacked_item() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}), msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
@ -601,16 +602,16 @@ async fn test_trade_partial_stacked_item() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}), msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
@ -727,32 +728,31 @@ async fn test_trade_individual_both() {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 8); assert_eq!(ack.len(), 8);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810001,
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210000,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810001,
item_id: 0x10000,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x210000,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810002,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message {
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
@ -761,19 +761,20 @@ async fn test_trade_individual_both() {
}), }),
.. ..
})))); }))));
assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810002,
client: 0,
item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 0, client: 0,
item_id: 0x10000,
item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810001,
.. ..
}), }),
.. ..
@ -899,30 +900,30 @@ async fn test_trade_stacked_both() {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 8); assert_eq!(ack.len(), 8);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_id: 0x810001,
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210000,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
item_id: 0x810001,
item_id: 0x10000,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x210000,
item_id: 0x810002,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message {
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x810002, item_id: 0x810002,
@ -930,18 +931,18 @@ async fn test_trade_stacked_both() {
}), }),
.. ..
})))); }))));
assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810002,
client: 0,
item_id: 0x810001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 0, client: 0,
item_id: 0x10000,
item_id: 0x810001,
.. ..
}), }),
.. ..
@ -1069,31 +1070,32 @@ async fn test_trade_partial_stack_both() {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 8); assert_eq!(ack.len(), 8);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_id: 0x810001,
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210000,
amount: 2,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
item_id: 0x810001,
item_id: 0x10000,
amount: 1,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x210000,
amount: 2,
item_id: 0x810002,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message {
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x810002, item_id: 0x810002,
@ -1101,19 +1103,18 @@ async fn test_trade_partial_stack_both() {
}), }),
.. ..
})))); }))));
assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810002,
client: 0,
item_id: 0x810001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 0, client: 0,
item_id: 0x10000,
amount: 1,
item_id: 0x810001,
.. ..
}), }),
.. ..
@ -1245,31 +1246,32 @@ async fn test_trade_same_stacked_item_to_eachother() {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 8); assert_eq!(ack.len(), 8);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_id: 0x810001,
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210000,
amount: 3,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
item_id: 0x810001,
item_id: 0x10000,
amount: 1,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x210000,
amount: 3,
item_id: 0x810002,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message {
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x810002, item_id: 0x810002,
@ -1277,19 +1279,18 @@ async fn test_trade_same_stacked_item_to_eachother() {
}), }),
.. ..
})))); }))));
assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810002,
client: 0,
item_id: 0x810001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 0, client: 0,
item_id: 0x10000,
amount: 1,
item_id: 0x810001,
.. ..
}), }),
.. ..
@ -1407,16 +1408,16 @@ async fn test_trade_stacked_when_already_have_partial_stack() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810001,
item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0,
item_id: 0x10000,
amount: 2,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x810001, item_id: 0x810001,
@ -1426,10 +1427,10 @@ async fn test_trade_stacked_when_already_have_partial_stack() {
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0,
item_id: 0x10000,
amount: 2,
msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810001,
item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
.. ..
}), }),
.. ..
@ -1554,32 +1555,31 @@ async fn test_trade_individual_for_stacked() {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 8); assert_eq!(ack.len(), 8);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
item_id: 0x810001,
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210000,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
item_id: 0x810001,
item_id: 0x10000,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x210000,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
item_id: 0x810002,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message {
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
@ -1588,19 +1588,20 @@ async fn test_trade_individual_for_stacked() {
}), }),
.. ..
})))); }))));
assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
item_id: 0x810002,
client: 0,
item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
item_id: 0x810001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 0, client: 0,
item_id: 0x10000,
item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
item_id: 0x810001,
.. ..
}), }),
.. ..
@ -1758,53 +1759,51 @@ async fn test_trade_multiple_individual() {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 14); assert_eq!(ack.len(), 14);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810001,
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210000,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810001,
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210000,
client: 0,
item_id: 0x10000,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810002,
item_id: 0x10001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810002,
client: 1,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810003,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[5], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x210001,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810003,
.. ..
}), }),
.. ..
@ -1812,8 +1811,8 @@ async fn test_trade_multiple_individual() {
assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810003,
item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810004,
.. ..
}), }),
.. ..
@ -1821,42 +1820,44 @@ async fn test_trade_multiple_individual() {
assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810003,
item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810004,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[8], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
assert!(matches!(ack[8], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0, client: 0,
item_id: 0x10000,
item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[9], (ClientId(1), SendShipPacket::Message(Message {
assert!(matches!(ack[9], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810004,
client: 0,
item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[10], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[10], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810004,
client: 0,
item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810002,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 0, client: 0,
item_id: 0x10001,
item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810002,
.. ..
}), }),
.. ..
@ -2030,49 +2031,49 @@ async fn test_trade_multiple_stacked() {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 14); assert_eq!(ack.len(), 14);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_id: 0x810001,
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210000,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_id: 0x810001,
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 1,
item_id: 0x210000,
client: 0,
item_id: 0x10000,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
item_id: 0x810002,
item_id: 0x10001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_id: 0x810002,
client: 1,
item_id: 0x810003,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[5], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x210001,
item_id: 0x810003,
.. ..
}), }),
.. ..
@ -2080,7 +2081,7 @@ async fn test_trade_multiple_stacked() {
assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message { assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x810003,
item_id: 0x810004,
.. ..
}), }),
.. ..
@ -2088,39 +2089,39 @@ async fn test_trade_multiple_stacked() {
assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1, client: 1,
item_id: 0x810003,
item_id: 0x810004,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[8], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
assert!(matches!(ack[8], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0, client: 0,
item_id: 0x10000,
item_id: 0x810001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[9], (ClientId(1), SendShipPacket::Message(Message {
assert!(matches!(ack[9], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810004,
client: 0,
item_id: 0x810001,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[10], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[10], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem { msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810004,
client: 0,
item_id: 0x810002,
.. ..
}), }),
.. ..
})))); }))));
assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
msg: GameMessage::CreateItem(CreateItem {
client: 0, client: 0,
item_id: 0x10001,
item_id: 0x810002,
.. ..
}), }),
.. ..
@ -2251,12 +2252,7 @@ async fn test_trade_not_enough_inventory_space_individual() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.err().unwrap(); })).await.err().unwrap();
match ack.downcast::<TransactionError<anyhow::Error>>().unwrap() {
TransactionError::Action(a) => {
assert_eq!(a.downcast::<TradeError>().unwrap(), TradeError::NoInventorySpace);
},
_ => panic!()
}
assert!(matches!(ack.downcast::<ItemStateError>().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull)));
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 2); assert_eq!(p1_items.items.len(), 2);
@ -2368,12 +2364,7 @@ async fn test_trade_not_enough_inventory_space_stacked() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.err().unwrap(); })).await.err().unwrap();
match ack.downcast::<TransactionError<anyhow::Error>>().unwrap() {
TransactionError::Action(a) => {
assert_eq!(a.downcast::<TradeError>().unwrap(), TradeError::NoInventorySpace);
},
_ => panic!()
}
assert!(matches!(ack.downcast::<ItemStateError>().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull)));
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items.len(), 1);
@ -2482,12 +2473,7 @@ async fn test_trade_stack_too_big() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.err().unwrap(); })).await.err().unwrap();
match ack.downcast::<TransactionError<anyhow::Error>>().unwrap() {
TransactionError::Action(a) => {
assert_eq!(a.downcast::<TradeError>().unwrap(), TradeError::NoStackSpace);
},
_ => panic!()
}
assert!(matches!(ack.downcast::<ItemStateError>().unwrap(), ItemStateError::InventoryError(InventoryError::StackFull)));
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items.len(), 1);
@ -2557,16 +2543,16 @@ async fn test_trade_meseta() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}), msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
@ -3109,12 +3095,7 @@ async fn test_invalid_trade_when_both_inventories_are_full() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.err().unwrap(); })).await.err().unwrap();
match ack.downcast::<TransactionError<anyhow::Error>>().unwrap() {
TransactionError::Action(a) => {
assert_eq!(a.downcast::<TradeError>().unwrap(), TradeError::NoInventorySpace);
},
_ => panic!()
}
assert!(matches!(ack.downcast::<ItemStateError>().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull)));
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 30); assert_eq!(p1_items.items.len(), 30);
@ -3124,6 +3105,7 @@ async fn test_invalid_trade_when_both_inventories_are_full() {
assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, ..}, ..))).count(), 30); assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, ..}, ..))).count(), 30);
} }
#[async_std::test] #[async_std::test]
async fn test_client_tries_to_start_two_trades() { async fn test_client_tries_to_start_two_trades() {
let mut entity_gateway = InMemoryGateway::default(); let mut entity_gateway = InMemoryGateway::default();
@ -3153,7 +3135,8 @@ async fn test_client_tries_to_start_two_trades() {
target: 0, target: 0,
trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0) trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0)
})))).await.err().unwrap(); })))).await.err().unwrap();
assert_eq!(ack.downcast::<TradeError>().unwrap(), TradeError::ClientAlreadyInTrade);
assert!(matches!(ack.downcast::<ShipError>().unwrap(), ShipError::TradeError(TradeError::ClientAlreadyInTrade)));
} }
#[async_std::test] #[async_std::test]
@ -3185,14 +3168,14 @@ async fn test_client_tries_trading_with_client_already_trading() {
target: 0, target: 0,
trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0) trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0)
})))).await.err().unwrap(); })))).await.err().unwrap();
assert_eq!(ack.downcast::<TradeError>().unwrap(), TradeError::OtherAlreadyInTrade);
assert!(matches!(ack.downcast::<ShipError>().unwrap(), ShipError::TradeError(TradeError::OtherAlreadyInTrade)));
let ack = ship.handle(ClientId(3), &RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { let ack = ship.handle(ClientId(3), &RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
client: 2, client: 2,
target: 0, target: 0,
trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 1) trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 1)
})))).await.err().unwrap(); })))).await.err().unwrap();
assert_eq!(ack.downcast::<TradeError>().unwrap(), TradeError::OtherAlreadyInTrade);
assert!(matches!(ack.downcast::<ShipError>().unwrap(), ShipError::TradeError(TradeError::OtherAlreadyInTrade)));
} }
#[async_std::test] #[async_std::test]
@ -3287,16 +3270,16 @@ async fn test_add_then_remove_individual_item() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}), msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
@ -3418,16 +3401,16 @@ async fn test_add_then_remove_stacked_item() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}), msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
@ -3632,16 +3615,16 @@ async fn test_add_then_remove_meseta() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}), msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
@ -4349,16 +4332,16 @@ async fn test_dropping_item_after_trade() {
let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed {
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert_eq!(ack.len(), 5); assert_eq!(ack.len(), 5);
assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}),
assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {..}), msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
msg: GameMessage::CreateItem(CreateItem {..}),
.. ..
})))); }))));
assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));

Loading…
Cancel
Save