From 66314688715075338c2116de3b3f88be54a07183 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 18 Jul 2022 18:42:44 -0600 Subject: [PATCH] trade refactor --- Cargo.toml | 1 + src/entity/gateway/entitygateway.rs | 4 + src/entity/gateway/inmemory.rs | 21 ++ src/entity/gateway/postgres/models.rs | 10 +- src/entity/item/mod.rs | 11 +- src/ship/items/actions.rs | 303 +++++++++++++++++- src/ship/packet/handler/trade.rs | 146 +++++---- src/ship/ship.rs | 10 +- src/ship/trade.rs | 10 +- tests/test_trade.rs | 433 +++++++++++++------------- 10 files changed, 649 insertions(+), 300 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 789571d..fb618d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ derive_more = { version = "0.99.3", features = ["display"]} thiserror = "1.0.15" ages-prs = "0.1" async-trait = "0.1.51" +async-recursion= "1.0.0" lazy_static = "1.4.0" barrel = { version = "0.6.5", features = ["pg"] } refinery = { version = "0.5.0", features = ["postgres"] } diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index fa04d3b..53b6fc4 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/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> { unimplemented!(); } + + async fn create_trade(&mut self, _char_id1: &CharacterEntityId, _char_id2: &CharacterEntityId) -> Result { + unimplemented!(); + } } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 8180fe0..f8b3c96 100644 --- a/src/entity/gateway/inmemory.rs +++ b/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().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(()) } } @@ -73,6 +76,7 @@ pub struct InMemoryGateway { equips: Arc>>, mag_modifiers: Arc>>>, weapon_modifiers: Arc>>>, + trades: Arc>>, } impl Default for InMemoryGateway { @@ -89,6 +93,7 @@ impl Default for InMemoryGateway { equips: Arc::new(Mutex::new(BTreeMap::new())), mag_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 mag_modifiers = self.mag_modifiers.lock().unwrap().clone(); let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone(); + let trades = self.trades.lock().unwrap().clone(); InMemoryGateway { users: Arc::new(Mutex::new(users)), @@ -178,6 +184,7 @@ impl EntityGateway for InMemoryGateway { equips: Arc::new(Mutex::new(equips)), mag_modifiers: Arc::new(Mutex::new(mag_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 mag_modifiers = self.mag_modifiers.lock().unwrap().clone(); let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone(); + let trades = self.trades.lock().unwrap().clone(); let working_gateway = InMemoryGateway { users: Arc::new(Mutex::new(users)), @@ -219,6 +227,7 @@ impl EntityGateway for InMemoryGateway { equips: Arc::new(Mutex::new(equips)), mag_modifiers: Arc::new(Mutex::new(mag_modifiers)), weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)), + trades: Arc::new(Mutex::new(trades)), }; let transaction = Box::new(InMemoryGatewayTransaction { @@ -482,4 +491,16 @@ impl EntityGateway for InMemoryGateway { Err(GatewayError::Error) } } + + async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result { + 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) + } } diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs index b54de2b..81296ae 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/src/entity/gateway/postgres/models.rs @@ -606,7 +606,7 @@ pub enum PgItemNoteDetail { }, SoldToShop, Trade { - id: u32, + trade_id: u32, character_to: u32, character_from: u32, }, @@ -647,8 +647,8 @@ impl From for PgItemNoteDetail { character_id: character_id.0, }, 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_from: character_from.0, }, @@ -695,8 +695,8 @@ impl From for ItemNote { character_id: CharacterEntityId(character_id), }, 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_from: CharacterEntityId(character_from as u32), }, diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 2a8dad4..6e51550 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -19,7 +19,7 @@ pub struct ItemEntityId(pub u32); pub struct ItemId(u32); #[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)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct TradeId(pub u32); #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -56,7 +56,7 @@ pub enum ItemNote { }, SoldToShop, Trade { - id: TradeId, + trade_id: TradeId, character_to: 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, +} diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index a717519..43f41b2 100644 --- a/src/ship/items/actions.rs +++ b/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, StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail}; 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::trade::TradeItem; +use crate::ship::location::{AreaClient, RoomId}; pub enum TriggerCreateItem { Yes, @@ -22,9 +21,9 @@ pub enum TriggerCreateItem { fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> + -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> { - move |(mut item_state, transaction), _| { + move |(mut item_state, transaction): (ItemStateProxy<'_>, Box) , _| { Box::pin(async move { let mut floor = item_state.floor(&character_id)?; let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; @@ -200,6 +199,23 @@ fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32) let mut inventory = item_state.inventory(&character_id)?; inventory.remove_meseta(amount)?; 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), ()) + -> Pin, Box), ()), 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), ())) }) @@ -419,6 +435,8 @@ where let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .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)) .commit((item_state_proxy, transaction)) .await?; @@ -780,6 +798,8 @@ where let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .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)) .commit((item_state_proxy, transaction)) .await?; @@ -832,3 +852,274 @@ where Ok((transaction, result)) }).await } + +#[async_recursion::async_recursion] +async fn iterate_inner<'a, I, O, T, F, FR>(state: (ItemStateProxy<'a>, Box), + mut input: Vec, + func: F, + arg: T) + -> Result<((ItemStateProxy<'a>, Box), Vec), 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), T) + -> Pin, Box), 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, + func: F) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), T) + -> Pin, Box), Vec), 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), T) + -> Pin, Box), 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), + mut input: Vec, + func: F) + -> Result<((ItemStateProxy<'a>, Box), Vec), ItemStateError> +where + 'a: 'async_recursion, + O: Send, + T: Clone + Send, + F: Fn((ItemStateProxy<'a>, Box), T) + -> Pin, Box), 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), Vec) + -> Pin, Box), Vec), ItemStateError>> + Send + 'a>> +where + O: Send, + T: Send + Clone + 'static + std::fmt::Debug, + F: for<'a> Fn((ItemStateProxy<'a>, Box), T) + -> Pin, Box), 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), T) + -> Pin, Box), ()), 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), ()) + -> Pin, Box), 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), InventoryItem) + -> Pin, Box), 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), Vec) + -> Pin, Box), Vec), 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), InventoryItem) + -> Pin, Box), 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, Meseta), + p2: (&AreaClient, &CharacterEntity, &Vec, Meseta)) + -> Result<(Vec, Vec), 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 +} diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index 040bc29..050241f 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -4,16 +4,20 @@ 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::items::ClientItemId; +use crate::ship::items::state::{ItemState, ItemStateError, InventoryItemDetail}; use crate::ship::trade::{TradeItem, TradeState, TradeStatus}; use crate::entity::gateway::EntityGateway; 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 OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF); - #[derive(thiserror::Error, Debug, PartialEq, Eq)] pub enum TradeError { #[error("no partner")] @@ -51,9 +55,9 @@ pub async fn trade_request(id: ClientId, target: u32, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, trades: &mut TradeState) - -> Result + Send>, anyhow::Error> + -> Result + Send>, ShipError> { let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet match trade_request.trade { @@ -114,18 +118,18 @@ pub async fn trade_request(id: ClientId, .with(&id, |this, other| -> Result + 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)?; + let inventory = item_state.get_character_inventory(&client.character)?; if ClientItemId(item_id) == MESETA_ITEM_ID { this.meseta += amount as usize; } else { - let item = inventory.get_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))); }, - InventoryItem::Stacked(stacked_item) => { + InventoryItemDetail::Stacked(stacked_item) => { if stacked_item.count() < amount as usize { 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 + 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()?; + let inventory = item_state.get_character_inventory(&client.character).ok()?; if ClientItemId(item_id) == MESETA_ITEM_ID { this.meseta -= amount as usize; } else { - let item = inventory.get_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| { item.item_id() != ClientItemId(item_id) }) }, - InventoryItem::Stacked(_stacked_item) => { + InventoryItemDetail::Stacked(_stacked_item) => { let trade_item_index = this.items.iter() .position(|item| { item.item_id() == ClientItemId(item_id) @@ -293,7 +297,7 @@ pub async fn inner_items_to_trade(id: ClientId, items_to_trade: &ItemsToTrade, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, trades: &mut TradeState) -> Result + 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 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 })) { 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()) } 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 { return Err(TradeError::InvalidMeseta.into()) } @@ -334,8 +338,8 @@ pub async fn inner_items_to_trade(id: ClientId, Ok(()) } 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 .iter() .find(|i| i.item_id() == ClientItemId(item.item_id)) @@ -345,28 +349,28 @@ pub async fn inner_items_to_trade(id: ClientId, .cloned().collect::>() .try_into() .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(()) } 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] { + 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; 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()) + Err(TradeError::InvalidStackAmount(real_item.item_id, amount).into()) } } else { - Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into()) + Err(TradeError::InvalidStackAmount(real_item.item_id, amount).into()) } } else { @@ -405,11 +409,11 @@ pub async fn items_to_trade(id: ClientId, items_to_trade_pkt: &ItemsToTrade, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, trades: &mut TradeState) -> Result + 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 { Ok(p) => Ok(p), Err(err) => { @@ -429,7 +433,7 @@ pub async fn trade_confirmed(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, trades: &mut TradeState) -> Result + Send>, anyhow::Error> where @@ -473,36 +477,63 @@ where Ok(Box::new(None.into_iter()) as Box + 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 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 clients_in_room = client_location.get_all_clients_by_client(id)?; - let traded_item_packets = traded_items + let (this_new_items, other_new_items) = trade_items(item_state, + entity_gateway, + room_id, + (&this_local_client, &this_client.character, &this.items, Meseta(this.meseta as u32)), + (&other_local_client, &other_client.character, &other.items, Meseta(other.meseta as u32))).await?; + + let create_item_packets = this_new_items .into_iter() - .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)) - ] - }, - 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)) - ] + .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()) }, + InventoryItemDetail::Stacked(stacked_item) => { + GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap()) + } } - }) + }); + + let meseta_packets = vec![(this_local_client, other_local_client, this.meseta), (other_local_client, this_local_client, other.meseta)] + .into_iter() + .filter(|(_, _, meseta)| *meseta != 0) + .flat_map(|(this, other, meseta)| { + [ + GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(this, MESETA_ITEM_ID, meseta as u32)), + GameMessage::CreateItem(builder::message::create_meseta(other, meseta)), + ] + }); + + let clients_in_room = client_location.get_all_clients_by_client(id)?; + let traded_item_packets = remove_item_packets + .chain(create_item_packets) + .chain(meseta_packets) .flat_map(move |packet| { clients_in_room .clone() @@ -521,6 +552,7 @@ where } }) }); + let close_trade = vec![ (this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())), (other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index b833024..81b5a31 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -78,8 +78,10 @@ pub enum ShipError { InvalidShip(usize), InvalidBlock(usize), InvalidItem(items::ClientItemId), - #[error("tradeerror {0}")] + #[error("trade error {0}")] TradeError(#[from] crate::ship::packet::handler::trade::TradeError), + #[error("trade state error {0}")] + TradeStateError(#[from] crate::ship::trade::TradeStateError), } #[derive(Debug)] @@ -569,7 +571,7 @@ impl ShipServerState { 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? + 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(); @@ -744,11 +746,11 @@ impl ServerState for ShipServerState { }, 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? + 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(_) => { 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) => { handler::settings::keyboard_config(id, keyboard_config, &mut self.clients, &mut self.entity_gateway).await diff --git a/src/ship/trade.rs b/src/ship/trade.rs index 39d7d0e..6871179 100644 --- a/src/ship/trade.rs +++ b/src/ship/trade.rs @@ -31,6 +31,13 @@ impl TradeItem { 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)] -#[error("")] pub enum TradeStateError { + #[error("client not in trade {0}")] ClientNotInTrade(ClientId), + #[error("mismatched trade {0} {1}")] MismatchedTrade(ClientId, ClientId), } diff --git a/tests/test_trade.rs b/tests/test_trade.rs index 65505b3..1da0049 100644 --- a/tests/test_trade.rs +++ b/tests/test_trade.rs @@ -2,10 +2,11 @@ use std::convert::TryInto; use elseware::common::serverstate::{ClientId, ServerState}; use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; use elseware::entity::item; -use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket}; +use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError}; use elseware::entity::item::{Meseta, ItemEntity}; use elseware::ship::items::transaction::TransactionError; use elseware::ship::packet::handler::trade::TradeError; +use elseware::ship::items::state::{ItemStateError, InventoryError}; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -190,16 +191,16 @@ async fn test_trade_one_individual_item() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); 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 {..}), .. })))); 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 {..}))); @@ -292,15 +293,15 @@ async fn test_trade_player2_to_player1() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); 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 {..}), .. })))); - 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 {..}))); @@ -392,16 +393,16 @@ async fn test_reverse_trade_ack_order() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); 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 {..}), .. })))); 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 {..}))); @@ -496,16 +497,16 @@ async fn test_trade_one_stacked_item() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); 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 {..}), .. })))); 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 {..}))); @@ -601,16 +602,16 @@ async fn test_trade_partial_stacked_item() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); 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 {..}), .. })))); 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 {..}))); @@ -727,32 +728,31 @@ async fn test_trade_individual_both() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 8); 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 { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { 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 { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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 { client: 1, 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 { - 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 { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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::>(); assert_eq!(ack.len(), 8); 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 { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, - item_id: 0x810001, + item_id: 0x10000, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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 { client: 1, 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 { - client: 1, - item_id: 0x810002, + client: 0, + item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x10000, + item_id: 0x810001, .. }), .. @@ -1069,31 +1070,32 @@ async fn test_trade_partial_stack_both() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 8); 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 { - msg: GameMessage::CreateItem(CreateItem { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, - item_id: 0x810001, + item_id: 0x10000, + amount: 1, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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 { client: 1, 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 { - client: 1, - item_id: 0x810002, + client: 0, + item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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::>(); assert_eq!(ack.len(), 8); 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 { - msg: GameMessage::CreateItem(CreateItem { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, - item_id: 0x810001, + item_id: 0x10000, + amount: 1, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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 { client: 1, 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 { - client: 1, - item_id: 0x810002, + client: 0, + item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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 { })).await.unwrap().collect::>(); 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 { client: 1, 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 { - 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::>(); assert_eq!(ack.len(), 8); 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 { - msg: GameMessage::CreateItem(CreateItem { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { 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 { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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 { client: 1, 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 { - 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 { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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::>(); assert_eq!(ack.len(), 14); 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 { - 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, - 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 { - 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, - 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 { msg: GameMessage::CreateItem(CreateItem { 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 { msg: GameMessage::CreateItem(CreateItem { 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, - 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 { - 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 { - 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 { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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::>(); assert_eq!(ack.len(), 14); 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 { - 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, - 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 { - 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, - 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 { msg: GameMessage::CreateItem(CreateItem { 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 { msg: GameMessage::CreateItem(CreateItem { 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, - 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 { - 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 { - client: 1, - item_id: 0x810004, + client: 0, + item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { 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 { })).await.err().unwrap(); - match ack.downcast::>().unwrap() { - TransactionError::Action(a) => { - assert_eq!(a.downcast::().unwrap(), TradeError::NoInventorySpace); - }, - _ => panic!() - } + assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); 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 { })).await.err().unwrap(); - match ack.downcast::>().unwrap() { - TransactionError::Action(a) => { - assert_eq!(a.downcast::().unwrap(), TradeError::NoInventorySpace); - }, - _ => panic!() - } + assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); 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 { })).await.err().unwrap(); - match ack.downcast::>().unwrap() { - TransactionError::Action(a) => { - assert_eq!(a.downcast::().unwrap(), TradeError::NoStackSpace); - }, - _ => panic!() - } + assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::StackFull))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); 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 { })).await.unwrap().collect::>(); 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 {..}), .. })))); 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 {..}))); @@ -3109,12 +3095,7 @@ async fn test_invalid_trade_when_both_inventories_are_full() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.err().unwrap(); - match ack.downcast::>().unwrap() { - TransactionError::Action(a) => { - assert_eq!(a.downcast::().unwrap(), TradeError::NoInventorySpace); - }, - _ => panic!() - } + assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); 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); } + #[async_std::test] async fn test_client_tries_to_start_two_trades() { let mut entity_gateway = InMemoryGateway::default(); @@ -3153,7 +3135,8 @@ async fn test_client_tries_to_start_two_trades() { target: 0, trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0) })))).await.err().unwrap(); - assert_eq!(ack.downcast::().unwrap(), TradeError::ClientAlreadyInTrade); + + assert!(matches!(ack.downcast::().unwrap(), ShipError::TradeError(TradeError::ClientAlreadyInTrade))); } #[async_std::test] @@ -3185,14 +3168,14 @@ async fn test_client_tries_trading_with_client_already_trading() { target: 0, trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0) })))).await.err().unwrap(); - assert_eq!(ack.downcast::().unwrap(), TradeError::OtherAlreadyInTrade); + assert!(matches!(ack.downcast::().unwrap(), ShipError::TradeError(TradeError::OtherAlreadyInTrade))); let ack = ship.handle(ClientId(3), &RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 2, target: 0, trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 1) })))).await.err().unwrap(); - assert_eq!(ack.downcast::().unwrap(), TradeError::OtherAlreadyInTrade); + assert!(matches!(ack.downcast::().unwrap(), ShipError::TradeError(TradeError::OtherAlreadyInTrade))); } #[async_std::test] @@ -3287,16 +3270,16 @@ async fn test_add_then_remove_individual_item() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); 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 {..}), .. })))); 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 {..}))); @@ -3418,16 +3401,16 @@ async fn test_add_then_remove_stacked_item() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); 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 {..}), .. })))); 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 {..}))); @@ -3632,16 +3615,16 @@ async fn test_add_then_remove_meseta() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); 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 {..}), .. })))); 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 {..}))); @@ -4349,16 +4332,16 @@ async fn test_dropping_item_after_trade() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); 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 {..}), .. })))); 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 {..})));