Browse Source

handle case where items are removed from shop after buying

pbs
jake 4 years ago
parent
commit
cebb6a5f31
  1. 2
      src/ship/items/manager.rs
  2. 34
      src/ship/packet/handler/direct_message.rs
  3. 119
      tests/test_shops.rs

2
src/ship/items/manager.rs

@ -763,7 +763,7 @@ impl ItemManager {
shop_item: &(dyn ShopItem + Send + Sync), shop_item: &(dyn ShopItem + Send + Sync),
item_id: ClientItemId, item_id: ClientItemId,
amount: usize) amount: usize)
-> Result<(&InventoryItem), ItemManagerError> {
-> Result<&InventoryItem, ItemManagerError> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let item_detail = shop_item.as_item(); let item_detail = shop_item.as_item();

34
src/ship/packet/handler/direct_message.rs

@ -10,7 +10,7 @@ use crate::ship::items::{ItemManager, ClientItemId, TriggerCreateItem, FloorItem
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use libpso::utf8_to_utf16_array; use libpso::utf8_to_utf16_array;
use crate::ship::packet::builder; use crate::ship::packet::builder;
use crate::ship::shops::ShopItem;
use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
const BANK_ACTION_DEPOSIT: u8 = 0; const BANK_ACTION_DEPOSIT: u8 = 0;
const BANK_ACTION_WITHDRAW: u8 = 1; const BANK_ACTION_WITHDRAW: u8 = 1;
@ -330,15 +330,26 @@ where
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
let item: &(dyn ShopItem + Send + Sync) = match buy_item.shop_type {
let (item, remove): (&(dyn ShopItem + Send + Sync), bool) = match buy_item.shop_type {
SHOP_OPTION_WEAPON => { SHOP_OPTION_WEAPON => {
client.weapon_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?
(client.weapon_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?, false)
}, },
SHOP_OPTION_TOOL => { SHOP_OPTION_TOOL => {
client.tool_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?
let item = client.tool_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?;
let remove = match item {
ToolShopItem::Tech(_) => true,
_ => false,
};
(item, remove)
}, },
SHOP_OPTION_ARMOR => { SHOP_OPTION_ARMOR => {
client.armor_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?
let item = client.armor_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?;
let remove = match item {
ArmorShopItem::Unit(_) => true,
_ => false,
};
(item, remove)
}, },
_ => { _ => {
return Err(ShipError::ShopError) return Err(ShipError::ShopError)
@ -353,9 +364,20 @@ where
entity_gateway.save_character(&client.character).await; entity_gateway.save_character(&client.character).await;
let inventory_item = item_manager.player_buys_item(entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as usize).await?; let inventory_item = item_manager.player_buys_item(entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as usize).await?;
let create = builder::message::create_withdrawn_inventory_item(area_client, inventory_item)?; let create = builder::message::create_withdrawn_inventory_item(area_client, inventory_item)?;
if remove {
match buy_item.shop_type {
SHOP_OPTION_TOOL => {
client.tool_shop.remove(buy_item.shop_index as usize);
},
SHOP_OPTION_ARMOR => {
client.armor_shop.remove(buy_item.shop_index as usize);
},
_ => {}
}
}
let other_clients_in_area = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?; let other_clients_in_area = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
Ok(Box::new(other_clients_in_area.into_iter() Ok(Box::new(other_clients_in_area.into_iter()
.map(move |c| { .map(move |c| {

119
tests/test_shops.rs

@ -360,7 +360,7 @@ async fn test_other_clients_see_stacked_purchase() {
async fn test_buying_item_without_enough_mseseta() { async fn test_buying_item_without_enough_mseseta() {
let mut entity_gateway = InMemoryGateway::new(); let mut entity_gateway = InMemoryGateway::new();
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let mut ship = ShipServerState::builder() let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone()) .gateway(entity_gateway.clone())
@ -445,5 +445,120 @@ async fn test_player_double_buys_from_tool_shop() {
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap(); let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
assert!(c1.meseta < 999999); assert!(c1.meseta < 999999);
let p1_items = entity_gateway.get_items_by_character(&char1).await; let p1_items = entity_gateway.get_items_by_character(&char1).await;
assert_eq!(p1_items.len(), 10);
assert_eq!(p1_items.len(), 9);
}
#[async_std::test]
async fn test_techs_disappear_from_shop_when_bought() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
char1.exp = 80000000;
char1.meseta = 999999;
entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
client: 255,
target: 255,
shop_type: 0,
})))).await.unwrap().collect::<Vec<_>>();
let first_tech = match &packets[0].1 {
SendShipPacket::Message(Message {msg: GameMessage::ShopList(shop_list)}) => {
shop_list.items.iter()
.enumerate()
.filter(|(_, item)| {
item.item_bytes[0] == 3 && item.item_bytes[1] == 2
})
.nth(0).unwrap().0
},
_ => panic!(""),
};
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
client: 255,
target: 255,
item_id: 0x10000,
shop_type: 0,
shop_index: first_tech as u8,
amount: 1,
unknown1: 0,
})))).await.unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
client: 255,
target: 255,
item_id: 0x10001,
shop_type: 0,
shop_index: first_tech as u8,
amount: 1,
unknown1: 0,
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1).await;
assert!(p1_items[0].item != p1_items[1].item);
}
#[async_std::test]
async fn test_units_disappear_from_shop_when_bought() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
char1.exp = 80000000;
char1.meseta = 999999;
entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::ShopRequest(ShopRequest {
client: 255,
target: 255,
shop_type: 2,
})))).await.unwrap().collect::<Vec<_>>();
let first_unit = match &packets[0].1 {
SendShipPacket::Message(Message {msg: GameMessage::ShopList(shop_list)}) => {
shop_list.items.iter()
.enumerate()
.filter(|(_, item)| {
item.item_bytes[0] == 1 && item.item_bytes[1] == 3
})
.nth(0).unwrap().0
},
_ => panic!(""),
};
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
client: 255,
target: 255,
item_id: 0x10000,
shop_type: 2,
shop_index: first_unit as u8,
amount: 1,
unknown1: 0,
})))).await.unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BuyItem(BuyItem {
client: 255,
target: 255,
item_id: 0x10001,
shop_type: 2,
shop_index: first_unit as u8,
amount: 1,
unknown1: 0,
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1).await;
assert!(p1_items[0].item != p1_items[1].item);
} }
Loading…
Cancel
Save