use std::convert::TryInto; use elseware::common::serverstate::{ClientId, ServerState}; use entity::gateway::{EntityGateway, InMemoryGateway}; use entity::item; use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError}; use entity::item::{Meseta, ItemEntity, InventoryItemEntity}; use elseware::ship::packet::handler::trade::TradeError; use libpso::packet::ship::*; use libpso::packet::messages::*; #[path = "common.rs"] mod common; use common::*; async fn initialize_trade(ship: &mut ShipServerState, client1: ClientId, client2: ClientId) { ship.handle(client1, RecvShipPacket::DirectMessage(DirectMessage::new(client2.0 as u32 -1, GameMessage::TradeRequest(TradeRequest { client: client1.0 as u8 -1, target: 0, trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0) })))).await.unwrap(); ship.handle(client2, RecvShipPacket::DirectMessage(DirectMessage::new(client1.0 as u32 -1, GameMessage::TradeRequest(TradeRequest { client: client2.0 as u8 -1, target: 0, trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Respond, 0) })))).await.unwrap(); } async fn confirm_trade(ship: &mut ShipServerState, client1: ClientId, client2: ClientId) { ship.handle(client1, RecvShipPacket::DirectMessage(DirectMessage::new(client2.0 as u32 -1, GameMessage::TradeRequest(TradeRequest { client: client1.0 as u8 -1, target: 0, trade: TradeRequestCommand::Confirm })))).await.unwrap(); ship.handle(client2, RecvShipPacket::DirectMessage(DirectMessage::new(client1.0 as u32 -1, GameMessage::TradeRequest(TradeRequest { client: client2.0 as u8 -1, target: 0, trade: TradeRequestCommand::Confirm })))).await.unwrap(); } async fn finalconfirm_trade(ship: &mut ShipServerState, client1: ClientId, client2: ClientId) { ship.handle(client1, RecvShipPacket::DirectMessage(DirectMessage::new(client2.0 as u32 -1, GameMessage::TradeRequest(TradeRequest { client: client1.0 as u8 -1, target: 0, trade: TradeRequestCommand::FinalConfirm })))).await.unwrap(); ship.handle(client2, RecvShipPacket::DirectMessage(DirectMessage::new(client1.0 as u32 -1, GameMessage::TradeRequest(TradeRequest { client: client2.0 as u8 -1, target: 0, trade: TradeRequestCommand::FinalConfirm })))).await.unwrap(); } #[derive(Default)] struct TradeItemBuilder { count: usize, items: [TradeItem; 32], } impl TradeItemBuilder { fn individual(mut self, item: &InventoryItemEntity, item_id: u32) -> Self { let idata = item.with_individual(|i| i.item.as_client_bytes()).unwrap(); self.items[self.count] = TradeItem { item_data: idata[0..12].try_into().unwrap(), item_id: item_id, item_data2: idata[12..16].try_into().unwrap(), }; self.count += 1; self } fn stacked(mut self, item: &InventoryItemEntity, item_id: u32, amount: u8) -> Self { let idata = item .with_stacked(|i| i[0].item.tool().unwrap().as_stacked_bytes(i.len())) .map(|mut data| { data[5] = amount; data }) .unwrap(); self.items[self.count] = TradeItem { item_data: idata[0..12].try_into().unwrap(), item_id: item_id, item_data2: idata[12..16].try_into().unwrap(), }; self.count += 1; self } fn meseta(mut self, amount: usize) -> Self { self.items[self.count] = TradeItem { item_data: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], item_id: 0xFFFFFFFF, item_data2: u32::to_le_bytes(amount as u32), }; self.count += 1; self } fn build(self) -> [TradeItem; 32] { self.items } } #[async_std::test] async fn test_trade_one_individual_item() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let mut p1_inv = Vec::new(); p1_inv.push(entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 0); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); } #[async_std::test] async fn test_trade_player2_to_player1() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let mut p2_inv = Vec::new(); p2_inv.push(entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 0); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x210000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .individual(&p2_items.items[0], 0x210000) .build(); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); } #[async_std::test] async fn test_reverse_trade_ack_order() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let mut p1_inv = Vec::new(); p1_inv.push(entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(2), ClientId(1)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(2), ClientId(1)).await; finalconfirm_trade(&mut ship, ClientId(2), ClientId(1)).await; let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 0); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); } #[async_std::test] async fn test_trade_one_stacked_item() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 2) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 2) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 0); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); } #[async_std::test] async fn test_trade_partial_stacked_item() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items[0].with_stacked(|i| i.len()).unwrap(), 2); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 1) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items[0].with_stacked(|i| i.len()).unwrap(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); assert_eq!(p2_items.items[0].with_stacked(|i| i.len()).unwrap(), 1); } #[async_std::test] async fn test_trade_individual_both() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_inv = vec![ entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()]; let p2_inv = vec![ entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()]; entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .individual(&p2_items.items[0], 0x210000) .build(); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 8); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 1, item_id: 0x210000, .. }), .. })))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, item_id: 0x10000, .. }), .. })))); assert!(matches!(ack[2], (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, .. }), .. })))); 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 item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[4], (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, .. }), .. })))); assert!(matches!(ack[5], (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[6], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[7], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert!(matches!(p1_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Handgun, ..}), ..})); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Saber, ..}), ..})); } #[async_std::test] async fn test_trade_stacked_both() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_stack = futures::future::join_all((0..3).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monofluid, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 2) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210000, 3) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 2) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .stacked(&p2_items.items[0], 0x210000, 3) .build(); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 8); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 1, item_id: 0x210000, .. }), .. })))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, item_id: 0x10000, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[6], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[7], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 3); assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..})); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2); assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); } #[async_std::test] async fn test_trade_partial_stack_both() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_stack = futures::future::join_all((0..3).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monofluid, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210000, 2) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 1) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .stacked(&p2_items.items[0], 0x210000, 2) .build(); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 8); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 1, item_id: 0x210000, amount: 2, .. }), .. })))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, item_id: 0x10000, amount: 1, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[6], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[7], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 1); assert_eq!(p1_items.items[1].with_stacked(|i| i.clone()).unwrap().len(), 2); assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); assert!(matches!(p1_items.items[1].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..})); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 2); assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 1); assert_eq!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap().len(), 1); assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..})); assert!(matches!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); } #[async_std::test] async fn test_trade_same_stacked_item_to_eachother() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack = futures::future::join_all((0..3).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_stack = futures::future::join_all((0..4).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210000, 3) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 1) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .stacked(&p2_items.items[0], 0x210000, 3) .build(); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 8); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 1, item_id: 0x210000, amount: 3, .. }), .. })))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, item_id: 0x10000, amount: 1, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[6], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[7], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 5); assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2); assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); } #[async_std::test] async fn test_trade_stacked_when_already_have_partial_stack() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack = futures::future::join_all((0..3).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_stack = futures::future::join_all((0..3).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 2) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 2) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); 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(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[2], (ClientId(2), 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[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 1); assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 5); assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); } #[async_std::test] async fn test_trade_individual_for_stacked() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_inv = vec![ entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()]; let p2_stack = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210000, 2) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .stacked(&p2_items.items[0], 0x210000, 2) .build(); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 8); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 1, item_id: 0x210000, .. }), .. })))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, item_id: 0x10000, .. }), .. })))); assert!(matches!(ack[2], (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, .. }), .. })))); 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], item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[4], (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, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), 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, .. }), .. })))); assert!(matches!(ack[6], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[7], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2); assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Saber, ..}), ..})); } #[async_std::test] async fn test_trade_multiple_individual() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_inv = vec![ entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Buster, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), ]; let p2_inv = vec![ entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Autogun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), ]; entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 2); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10001, 1) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210000, 1) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210001, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .individual(&p1_items.items[1], 0x10001) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 2, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .individual(&p2_items.items[0], 0x210000) .individual(&p2_items.items[1], 0x210001) .build(); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 2, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 14); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 1, item_id: 0x210000, .. }), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 1, item_id: 0x210001, .. }), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, item_id: 0x10000, .. }), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, item_id: 0x10001, .. }), .. })))); 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: 0x810004, .. }), .. })))); assert!(matches!(ack[5], (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: 0x810004, .. }), .. })))); assert!(matches!(ack[6], (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: 0x810003, .. }), .. })))); assert!(matches!(ack[7], (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: 0x810003, .. }), .. })))); assert!(matches!(ack[8], (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: 0x810002, .. }), .. })))); assert!(matches!(ack[9], (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: 0x810002, .. }), .. })))); assert!(matches!(ack[10], (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: 0x810001, .. }), .. })))); assert!(matches!(ack[11], (ClientId(2), 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: 0x810001, .. }), .. })))); assert!(matches!(ack[12], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[13], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); assert!(matches!(p1_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Handgun, ..}), ..})); assert!(matches!(p1_items.items[1].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Autogun, ..}), ..})); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 2); assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Saber, ..}), ..})); assert!(matches!(p2_items.items[1].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Buster, ..}), ..})); } #[async_std::test] async fn test_trade_multiple_stacked() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack1 = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p1_stack2 = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Dimate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_stack1 = futures::future::join_all((0..3).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monofluid, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_stack2 = futures::future::join_all((0..3).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Difluid, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack1, p1_stack2])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack1, p2_stack2])).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 2); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 2) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10001, 2) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210000, 3) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210001, 3) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 2) .stacked(&p1_items.items[1], 0x10001, 2) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 2, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .stacked(&p2_items.items[0], 0x210000, 3) .stacked(&p2_items.items[1], 0x210001, 3) .build(); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 2, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 14); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 1, item_id: 0x210000, .. }), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 1, item_id: 0x210001, .. }), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, item_id: 0x10000, .. }), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, item_id: 0x10001, .. }), .. })))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810004, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810004, .. }), .. })))); assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810003, .. }), .. })))); assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810003, .. }), .. })))); assert!(matches!(ack[8], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[9], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[10], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 0, item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[12], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[13], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 3); assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..})); assert_eq!(p1_items.items[1].with_stacked(|i| i.clone()).unwrap().len(), 3); assert!(matches!(p1_items.items[1].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Difluid, ..}), ..})); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 2); assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2); assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); assert_eq!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap().len(), 2); assert!(matches!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Dimate, ..}), ..})); } #[async_std::test] async fn test_trade_not_enough_inventory_space_individual() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_inv = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), } ).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_inv = futures::future::join_all((0..30).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), } ).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 30); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack, vec![ (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})), (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})), ]); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 30); } #[async_std::test] async fn test_trade_not_enough_inventory_space_stacked() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_inv = futures::future::join_all((0..30).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), } ).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 30); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 2) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 2) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack, vec![ (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})), (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})), ]); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 30); } #[async_std::test] async fn test_trade_stack_too_big() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack = futures::future::join_all((0..8).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_stack = futures::future::join_all((0..7).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items[0].with_stacked(|i| i.len()).unwrap(), 8); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); assert_eq!(p2_items.items[0].with_stacked(|i| i.len()).unwrap(), 7); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 4) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 4) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack, vec![ (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})), (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})), ]); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items[0].with_stacked(|i| i.len()).unwrap(), 8); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); assert_eq!(p2_items.items[0].with_stacked(|i| i.len()).unwrap(), 7); } #[async_std::test] async fn test_trade_meseta() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, Meseta(2323)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0xFFFFFF01, 23) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .meseta(23) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); assert_eq!(c1_meseta, Meseta(2300)); let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap(); assert_eq!(c2_meseta, Meseta(23)); } #[async_std::test] async fn test_trade_too_much_meseta() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, Meseta(4000)).await.unwrap(); entity_gateway.set_character_meseta(&char2.id, Meseta(999000)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0xFFFFFF01, 2000) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .meseta(2000) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); assert_eq!(c1_meseta, Meseta(4000)); let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap(); assert_eq!(c2_meseta, Meseta(999000)); } #[async_std::test] async fn test_trade_invalid_amount_of_meseta() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, Meseta(4000)).await.unwrap(); entity_gateway.set_character_meseta(&char2.id, Meseta(999000)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0xFFFFFF01, 5000) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .meseta(5000) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); assert_eq!(c1_meseta, Meseta(4000)); let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap(); assert_eq!(c2_meseta, Meseta(999000)); } #[async_std::test] async fn test_trade_meseta_request_and_items_dont_match() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, Meseta(4000)).await.unwrap(); entity_gateway.set_character_meseta(&char2.id, Meseta(999000)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0xFFFFFF01, 50) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .meseta(23) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); assert_eq!(c1_meseta, Meseta(4000)); let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap(); assert_eq!(c2_meseta, Meseta(999000)); } #[async_std::test] async fn test_player_declined_trade() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; let ack = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::Cancel })))).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); } #[async_std::test] async fn test_back_out_of_trade_last_minute() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let mut p1_inv = Vec::new(); p1_inv.push(entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let ack = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::Cancel })))).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); } #[async_std::test] async fn test_valid_trade_when_both_inventories_are_full() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_inv = futures::future::join_all((0..30).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), } ).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_inv = futures::future::join_all((0..30).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), } ).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 30); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 30); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10001, 1) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210000, 1) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210001, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .individual(&p1_items.items[1], 0x10001) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 2, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .individual(&p2_items.items[0], 0x210000) .individual(&p2_items.items[1], 0x210001) .build(); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 2, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 14); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 30); assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, ..}, ..))).count(), 28); assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, ..}, ..))).count(), 2); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 30); assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, ..}, ..))).count(), 2); 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(), 28); } #[async_std::test] async fn test_invalid_trade_when_both_inventories_are_full() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_inv = futures::future::join_all((0..30).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), } ).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p2_inv = futures::future::join_all((0..30).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), } ).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_inv)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 30); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 30); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10001, 1) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10002, 1) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210000, 1) })))).await.unwrap(); ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::AddItem(0x210001, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .individual(&p1_items.items[1], 0x10001) .individual(&p1_items.items[1], 0x10002) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 3, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let titems = TradeItemBuilder::default() .individual(&p2_items.items[0], 0x210000) .individual(&p2_items.items[1], 0x210001) .build(); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 2, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack, vec![ (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})), (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})), ]); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 30); assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, ..}, ..))).count(), 30); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 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 fn test_client_tries_to_start_two_trades() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let (_user2, _char3) = new_user_character(&mut entity_gateway, "a3", "a", 1).await; let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(3), "a3", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; join_lobby(&mut ship, ClientId(3)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; join_room(&mut ship, ClientId(3), 0).await; initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; let ack = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 0, target: 0, trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0) })))).await.err().unwrap(); assert!(matches!(ack.downcast::().unwrap(), TradeError::ClientAlreadyInTrade)); } #[async_std::test] async fn test_client_tries_trading_with_client_already_trading() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let (_user2, _char3) = new_user_character(&mut entity_gateway, "a3", "a", 1).await; let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; log_in_char(&mut ship, ClientId(3), "a3", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; join_lobby(&mut ship, ClientId(3)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; join_room(&mut ship, ClientId(3), 0).await; initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; let ack = ship.handle(ClientId(3), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest { client: 2, target: 0, trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0) })))).await.err().unwrap(); assert!(matches!(ack.downcast::().unwrap(), 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!(matches!(ack.downcast::().unwrap(), TradeError::OtherAlreadyInTrade)); } #[async_std::test] async fn test_add_then_remove_individual_item() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let mut p1_inv = Vec::new(); for _ in 0..2 { p1_inv.push(entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()); } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10001, 1) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::RemoveItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[1], 0x10001) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); } #[async_std::test] async fn test_add_then_remove_stacked_item() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack1 = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p1_stack2 = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monofluid, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack1, p1_stack2])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 2) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10001, 2) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::RemoveItem(0x10000, 2) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[1], 0x10001, 2) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 1); assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2); assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..})); } #[async_std::test] async fn test_add_then_remove_partial_stack() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack1 = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); let p1_stack2 = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monofluid, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack1, p1_stack2])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 2) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10001, 2) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::RemoveItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 1) .stacked(&p1_items.items[1], 0x10001, 2) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 2, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 8); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 1); assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 2); assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 1); assert_eq!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap().len(), 2); assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); assert!(matches!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..})); } #[async_std::test] async fn test_add_then_remove_meseta() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, Meseta(2323)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0xFFFFFF01, 23) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::RemoveItem(0xFFFFFF01, 5) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .meseta(18) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); assert_eq!(c1_meseta, Meseta(2305)); let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap(); assert_eq!(c2_meseta, Meseta(18)); } #[async_std::test] async fn test_items_to_trade_data_does_not_match() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let mut p1_inv = Vec::new(); p1_inv.push(entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let new_item = item::InventoryItemEntity::Individual( ItemEntity { id: p1_items.items[0].with_individual(|i| i.id).unwrap(), item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 2, special: None, attrs: [None, None, None], tekked: true, } )}); let titems = TradeItemBuilder::default() .individual(&new_item, 0x10000) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); } #[async_std::test] async fn test_items_to_trade_id_does_not_match() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let mut p1_inv = Vec::new(); p1_inv.push(entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10001) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); } #[async_std::test] async fn test_stack_is_same_amount_in_request_and_items_to_trade() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 2) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 1) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); } #[async_std::test] async fn test_stack_is_same_amount_in_request_and_items_to_trade2() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_stack = futures::future::join_all((0..2).map(|_| { let mut entity_gateway = entity_gateway.clone(); async move { entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { tool: item::tool::ToolType::Monomate, } ) }).await }})) .await .into_iter() .collect::,_>>() .unwrap(); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .stacked(&p1_items.items[0], 0x10000, 2) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); } #[async_std::test] async fn test_items_to_trade_count_less_than() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_inv = vec![ entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Brand, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Buster, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), ]; entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 3); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10001, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .individual(&p1_items.items[1], 0x10001) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 3); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); } #[async_std::test] async fn test_items_to_trade_count_greater_than() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, Meseta(23)).await.unwrap(); let p1_inv = vec![ entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Brand, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Buster, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), ]; entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 3); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10001, 1) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0xFFFFFF01, 5) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .individual(&p1_items.items[1], 0x10001) .meseta(5) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 4, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 3); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); } #[async_std::test] async fn test_items_to_trade_count_mismatch_with_meseta() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let p1_inv = vec![ entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Brand, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Buster, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap(), ]; entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 3); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10001, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .individual(&p1_items.items[1], 0x10001) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 3, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..)))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..)))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 3); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); } #[async_std::test] async fn test_dropping_item_after_trade() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await; let mut p1_inv = Vec::new(); p1_inv.push(entity_gateway.create_item( item::NewItemEntity { item: item::ItemDetail::Weapon( item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, grind: 0, special: None, attrs: [None, None, None], tekked: true, } ), }).await.unwrap()); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap(); entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::::new())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) .build()); log_in_char(&mut ship, ClientId(1), "a1", "a").await; log_in_char(&mut ship, ClientId(2), "a2", "a").await; join_lobby(&mut ship, ClientId(1)).await; join_lobby(&mut ship, ClientId(2)).await; create_room(&mut ship, ClientId(1), "room", "").await; join_room(&mut ship, ClientId(2), 0).await; let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); initialize_trade(&mut ship, ClientId(1), ClientId(2)).await; ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 1, target: 0, trade: TradeRequestCommand::AddItem(0x10000, 1) })))).await.unwrap(); confirm_trade(&mut ship, ClientId(1), ClientId(2)).await; finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await; let titems = TradeItemBuilder::default() .individual(&p1_items.items[0], 0x10000) .build(); let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 1, unknown2: 0, count: 1, items: titems, })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade { trade_target: 0, unknown2: 0, count: 0, items: Default::default(), })).await.unwrap(); assert_eq!(ack.len(), 2); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))); let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 0); let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..}))); let _ack = ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem { client: 0, target: 0, unknown1: 0, map_area: 0, item_id: 0x810001, x: 0.0, y: 0.0, z: 0.0, })))).await.unwrap(); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 0); let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); assert_eq!(p2_items.items.len(), 0); }